Errors and Retries
A lost response can follow a successful payment. Before retrying, determine whether the previous attempt dispatched a payment authorization.
Error Interfaces
| API | Failure interface |
|---|---|
payAndFetch | A PayResult with ok: false, reason, and optional detail. |
useX402Payment().fetch | A rejected promise plus the hook's error and status state. |
createBudgetAccount().fetch | A rejected promise; budget checks use X402Error. |
| Tab operations | Rejected promises, including typed tab errors and HTTP failures. |
These contracts refer to @dexterai/x402 version 6.0.0. Unexpected application errors still need normal exception handling.
Read a PayResult
An unpaid response can be a successful free request or an HTTP error. Check its status. On a paid result, the transaction signature is optional; retain it when present.
Retry Decisions
reason | Meaning | Next action |
|---|---|---|
unsupported_network | The client cannot use the offered network. | Supply a compatible wallet or choose another endpoint. |
insufficient_funds | The wallet cannot cover the payment. | Check the charged asset and network before funding. |
no_payment_options | No supported payment option was recognized. | Inspect the endpoint's 402 challenge. |
budget_exceeded | The amount exceeds the configured cap. | Review the price and cap. No payment was dispatched by this attempt. |
timeout | The SDK classified a timeout before payment dispatch. | Address the timeout and retry. |
merchant_rejected | The server reports rejecting the payment. | Inspect detail and the payment terms before retrying. |
settlement_failed | The server reports a settlement failure. | Reconcile with the seller or facilitator before another attempt. |
payment_unconfirmed | Authorization was sent; settlement could not be confirmed. | Reconcile first. A fresh attempt can authorize another charge. |
error | Unclassified failure. | Preserve detail and investigate; payment state is unknown. |
For an uncertain attempt, retain the endpoint, request time, wallet, amount, and any request ID or transaction identifier. Give that evidence to the seller or inspect the corresponding chain transaction. HTTP status alone does not establish that funds stayed in the wallet.
Timeouts and Missing Responses
payAndFetch separates a pre-payment timeout (timeoutMs, default 15000) from the wait after dispatch (responseTimeoutMs, default 120000). A post-dispatch timeout can return payment_unconfirmed, or a paid result with response: undefined if settlement was confirmed.
Treat response: undefined on a paid result as a delivery problem. Use the transaction evidence to request recovery from the seller. Paying again creates a separate payment attempt.
A reason: 'timeout' is a result classification, not a guarantee that the entire function finishes within timeoutMs. Apply application deadlines to the initial fetch and handle aborts without assuming they cancel a payment already submitted.
React and Budget Errors
Catch the promise rejection in your event handler or worker. The React hook records an error for display; reset() clears that UI state and transaction fields. It does not cancel or reverse an earlier payment.
The PayResult.reason table above belongs to payAndFetch. Do not assume the same strings appear on every X402Error. In particular, inspect the wrapper's error code and dispatch context before adding an automatic retry loop.
Tab Errors
| Class | Fields | Handling |
|---|---|---|
SessionScopeExceededError | reason | Stop spending when the cap, expiry, or counterparty check fails. |
TabClosedError | channelId | Stop using the closed handle. |
UnsupportedNetworkError | network | Use a network supported by the tab adapter. |
LiveSessionExistsError | details | Inspect and resolve the existing session before opening another for that seller. |
HistoricalV1MigrationRequiredError | surface, code | Replace the historical grant with an owner-approved V2 session. |
Tab operations can also throw ordinary errors for rejected HTTP requests, expired sessions, or transport failures. Preserve the original error when classification falls through.
onLiveSession: 'replace' authorizes replacement of an existing session. Resolve its signed but unsettled vouchers first: replacement can invalidate vouchers beyond the on-chain settled or crystallized amount. A grant-backed tab's close() settles without revoking its session; the wallet owner must revoke it separately. See Node.js Tabs.
Last Updated: