Skip to content

Error reference

tx402 raises 15 typed errors (SPEC §8) and the CLI reports them through 9 exit codes (SPEC §11). Both tables below are generated from the shipped source — TX402_ERROR_TAXONOMY and EXIT_CODE_BY_ERROR — so a code documented here is a code the binary actually returns.

Every error is an instance of Tx402Error (TypeScript) or Tx402Error (Python) and answers to isTx402Error / is_tx402_error. Catch by class when you want one, by predicate when you want all of them.

Code Class Exit Retryable
TX402_CONFIG_INVALID ConfigurationError 2 no
TX402_RESERVED_HEADER ReservedHeaderError 2 no
TX402_NON_REPLAYABLE NonReplayableRequestError 2 no
TX402_PROTOCOL_UNSUPPORTED UnsupportedProtocolError 5 no
TX402_SCHEME_UNSUPPORTED UnsupportedSchemeError 5 no
TX402_PAYMENT_REQUIRED_INVALID InvalidPaymentRequiredError 5 no
TX402_POLICY_BUDGET BudgetExceededError 3 no
TX402_POLICY_DOMAIN DomainNotAllowedError 3 no
TX402_LIQUIDITY InsufficientLiquidityError 4 no
TX402_SIGNER SignerError 6 no
TX402_CLOCK_SKEW ClockSkewError 5 no
TX402_PAYMENT_AMBIGUOUS AmbiguousPaymentError 8 no
TX402_RESOURCE_DELIVERY ResourceDeliveryError 9 no
TX402_REDIRECT_BLOCKED PaidRedirectBlockedError 8 no
TX402_TRANSPORT TransportError 7 yes

A shell script’s if [ $? -eq 3 ] is a public API, so this mapping is stable and changing a row is a breaking change. The grouping principle is what the operator has to change to make it work — not error severity, and not which layer raised it.

Exit 1 is deliberately never used: it is the runtime’s own crash code, and conflating “tx402 refused” with “the interpreter died” would make a script unable to tell them apart.

Exit Meaning What to do
0 success The call completed. Under --json, ok may still be false if the merchant answered non-2xx.
2 usage / config The invocation or the environment is wrong. Fix the command.
3 policy tx402’s own guardrail refused. Raise the cap or accept the refusal.
4 liquidity The wallet cannot cover it. Fund it.
5 protocol This client and this merchant cannot agree on the challenge. Nothing local helps.
6 signer The key or the signing device failed.
7 transport The network failed. Retryable under caller policy.
8 ambiguous payment Money may have moved and tx402 cannot tell. Never retry blindly.
9 resource failure Payment was fine; the resource was not delivered.

Grouped the other way — which errors produce which code:

  • 2TX402_CONFIG_INVALID, TX402_RESERVED_HEADER, TX402_NON_REPLAYABLE
  • 3TX402_POLICY_BUDGET, TX402_POLICY_DOMAIN
  • 4TX402_LIQUIDITY
  • 5TX402_PROTOCOL_UNSUPPORTED, TX402_SCHEME_UNSUPPORTED, TX402_PAYMENT_REQUIRED_INVALID, TX402_CLOCK_SKEW
  • 6TX402_SIGNER
  • 7TX402_TRANSPORT
  • 8TX402_PAYMENT_AMBIGUOUS, TX402_REDIRECT_BLOCKED
  • 9TX402_RESOURCE_DELIVERY

ConfigurationError · exit 2 (usage / config) · retryable: false

Retryability: no — Never. The condition will not change on its own.

Always carries (SPEC §8 required context): configPath, reason

These keys are guaranteed present in error.details, so a handler can read them without a existence check. Everything in details is redaction-safe by construction: identifiers, atomic amounts, and categories, never a signature, a key, or an authorization payload.

ReservedHeaderError · exit 2 (usage / config) · retryable: false

Retryability: no — Never. The condition will not change on its own.

Always carries (SPEC §8 required context): headerName

These keys are guaranteed present in error.details, so a handler can read them without a existence check. Everything in details is redaction-safe by construction: identifiers, atomic amounts, and categories, never a signature, a key, or an authorization payload.

NonReplayableRequestError · exit 2 (usage / config) · retryable: false

Retryability: no — Never. The condition will not change on its own.

Always carries (SPEC §8 required context): reason

These keys are guaranteed present in error.details, so a handler can read them without a existence check. Everything in details is redaction-safe by construction: identifiers, atomic amounts, and categories, never a signature, a key, or an authorization payload.

UnsupportedProtocolError · exit 5 (protocol) · retryable: false

Retryability: no — Never. The condition will not change on its own.

Always carries (SPEC §8 required context): observedVersion, supportedVersions

These keys are guaranteed present in error.details, so a handler can read them without a existence check. Everything in details is redaction-safe by construction: identifiers, atomic amounts, and categories, never a signature, a key, or an authorization payload.

UnsupportedSchemeError · exit 5 (protocol) · retryable: false

Retryability: no — Never. The condition will not change on its own.

Always carries (SPEC §8 required context): offeredSchemes, offeredNetworks

These keys are guaranteed present in error.details, so a handler can read them without a existence check. Everything in details is redaction-safe by construction: identifiers, atomic amounts, and categories, never a signature, a key, or an authorization payload.

InvalidPaymentRequiredError · exit 5 (protocol) · retryable: false

Retryability: no — Never. The condition will not change on its own.

Always carries (SPEC §8 required context): reason, schemaPath

These keys are guaranteed present in error.details, so a handler can read them without a existence check. Everything in details is redaction-safe by construction: identifiers, atomic amounts, and categories, never a signature, a key, or an authorization payload.

BudgetExceededError · exit 3 (policy) · retryable: false

Retryability: no — Never. The condition will not change on its own.

Always carries (SPEC §8 required context): requestedAtomic, capAtomic, committedAtomic, reservedAtomic, capKind

These keys are guaranteed present in error.details, so a handler can read them without a existence check. Everything in details is redaction-safe by construction: identifiers, atomic amounts, and categories, never a signature, a key, or an authorization payload.

DomainNotAllowedError · exit 3 (policy) · retryable: false

Retryability: no — Never. The condition will not change on its own.

Always carries (SPEC §8 required context): normalizedHost

These keys are guaranteed present in error.details, so a handler can read them without a existence check. Everything in details is redaction-safe by construction: identifiers, atomic amounts, and categories, never a signature, a key, or an authorization payload.

InsufficientLiquidityError · exit 4 (liquidity) · retryable: false

Retryability: conditional — Only after the underlying condition changes — fund the wallet, fix the signer.

Always carries (SPEC §8 required context): deficits

These keys are guaranteed present in error.details, so a handler can read them without a existence check. Everything in details is redaction-safe by construction: identifiers, atomic amounts, and categories, never a signature, a key, or an authorization payload.

SignerError · exit 6 (signer) · retryable: false

Retryability: conditional — Only after the underlying condition changes — fund the wallet, fix the signer.

Always carries (SPEC §8 required context): signerKind, causeCategory

These keys are guaranteed present in error.details, so a handler can read them without a existence check. Everything in details is redaction-safe by construction: identifiers, atomic amounts, and categories, never a signature, a key, or an authorization payload.

ClockSkewError · exit 5 (protocol) · retryable: false

Retryability: after-correction — Only after the clock is corrected. tx402 never adjusts the system clock.

Always carries (SPEC §8 required context): observedSkewMs, thresholdMs

These keys are guaranteed present in error.details, so a handler can read them without a existence check. Everything in details is redaction-safe by construction: identifiers, atomic amounts, and categories, never a signature, a key, or an authorization payload.

AmbiguousPaymentError · exit 8 (ambiguous payment) · retryable: false

Retryability: no-automatic-retryNever automatically. Money may have moved. Reconcile with the merchant first.

Always carries (SPEC §8 required context): reservationExpiresAtEpochMs, causeCategory

These keys are guaranteed present in error.details, so a handler can read them without a existence check. Everything in details is redaction-safe by construction: identifiers, atomic amounts, and categories, never a signature, a key, or an authorization payload.

ResourceDeliveryError · exit 9 (resource failure) · retryable: false

Retryability: app-dependent — The caller decides; tx402 has no way to know whether the resource is idempotent.

Always carries (SPEC §8 required context): status, reason

These keys are guaranteed present in error.details, so a handler can read them without a existence check. Everything in details is redaction-safe by construction: identifiers, atomic amounts, and categories, never a signature, a key, or an authorization payload.

PaidRedirectBlockedError · exit 8 (ambiguous payment) · retryable: false

Retryability: no — Never. The condition will not change on its own.

Always carries (SPEC §8 required context): fromOrigin, toOrigin

These keys are guaranteed present in error.details, so a handler can read them without a existence check. Everything in details is redaction-safe by construction: identifiers, atomic amounts, and categories, never a signature, a key, or an authorization payload.

TransportError · exit 7 (transport) · retryable: true

Retryability: caller-policy — Yes, under the caller’s own backoff policy. This is the only retryable: true row.

Always carries (SPEC §8 required context): causeCategory

These keys are guaranteed present in error.details, so a handler can read them without a existence check. Everything in details is redaction-safe by construction: identifiers, atomic amounts, and categories, never a signature, a key, or an authorization payload.