You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(OUT-3544): strict retry classifier for non-idempotent write paths
The OUT-3544 broadening of the retry classifier (429 → 429 + 5xx + network)
applies uniformly to every wrapped call, including QBO write endpoints
(`createInvoice`, `createCustomer`, `createItem`, `createPayment`,
`createAccount`, `createPurchase`) which have no Intuit-side request-key
dedupe. A 5xx or network error after the upstream commits could cause
pRetry to replay the write and produce a duplicate financial record.
Adds an `idempotent` option to `withRetry` / `isRetryableError`. In strict
mode (`idempotent: false`), the classifier retries only on 429 and explicit
`RetryableError.retry === true`; 5xx, network codes, and AbortSignal
timeouts are all treated as possibly-after-commit and never replayed.
Inverts the convention in both QBO-facing wrappers:
- `IntuitAPI.wrapWithRetry` defaults to strict; `customQuery` (the only
read in the wrapped set) opts back into broad retry. All 13 wrapped
writes (create/update/void/delete) inherit strict by default, so any
future write method added without options is automatically safe.
- `Intuit.wrapWithRetry` (OAuth) also defaults to strict. `createToken`
and `refreshAccessToken` consume single-use credentials and would
previously misdiagnose a 5xx-after-commit refresh as `invalid_grant`
via `tokenRefresh.handleInvalidGrant`, throwing `QBReconnectRequiredError`
for a healthy connection. Strict mode lets the transport error surface
honestly.
Both wrappers merge `{ idempotent: false, ...options }` rather than
relying on a parameter default, so an explicitly-passed `{}` cannot
silently flip back to the global broad-retry default.
Out of scope (not addressed here):
- The same after-commit-with-dropped-response can still produce a
duplicate when the 3-hour resync re-runs the write. Eliminating that
requires QBO's `requestid` query parameter on POST endpoints — a
separate ticket.
- `copilotAPI.ts` / `authenticate.ts` retain the global `idempotent: true`
default. None of those calls are QBO writes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
0 commit comments