NERONERO 402
Reference

Error codes

Stable identifier strings returned by the verifier and the settler.

Both the verifier (/verify) and the settler (/settle) return string error identifiers that merchants and SDKs can match on programmatically. The codes are stable; changing one is a breaking change.

(V) indicates the code may be returned by /verify. (S) indicates /settle. (V, S) indicates both.

Verification + settlement codes

  • invalid_envelope (V)paymentPayload does not match the V2 envelope schema.
  • unsupported_scheme (V, S) — the facilitator does not handle this scheme.
  • requirements_mismatch (V, S) — the accepted block does not match the merchant's paymentRequirements.
  • network_mismatch (V)accepted.network differs from the merchant's requirement.
  • amount_mismatch (V)accepted.amount differs from the merchant's requirement.
  • asset_mismatch (V)accepted.asset differs from the merchant's requirement.
  • payTo_mismatch (V)accepted.payTo differs from the merchant's requirement.
  • invalid_inner_payload (V)payload.userOp or payload.settlementCallSpec is malformed.
  • calldata_decode_failed (V)userOp.callData does not decode to execute(...) or to an executeBatch(...) containing a settle(...) invocation.
  • spec_mismatch (V) — the decoded settle arguments differ from payload.settlementCallSpec.
  • replay (V, S) — the requestHash has already been settled.

Settlement-only codes

  • in_flight (S) — another settlement for this requestHash is in progress. Caller should retry after the indicated Retry-After interval.
  • bundler_error (S) — the bundler rejected the UserOp. Causes include rate-limit rejection, simulation revert, and RPC error.
  • user_op_failed (S) — the UserOp executed on chain but reverted. The message field carries the surfaced revert reason.
  • receipt_timeout (S) — the bundler did not return a receipt within the configured deadline. The UserOp may still settle later; idempotency on requestHash ensures a retry will not double-spend.

Internal

  • internal_error (V, S) — facilitator-side fault. The client SHOULD retry once before treating this as a hard failure.

Reserved

Codes outside this list are reserved. New scheme implementations and new failure modes SHOULD allocate a new code rather than overload an existing one.

Where the codes appear

For /verify: {isValid: false, invalidReason: "...", details?: ...}. The details field, when present, carries machine-readable specifics (a Zod issue list for envelope errors, a sub-mismatch identifier for spec_mismatch).

For /settle: {success: false, x402Version: 2, errorCode: "...", message: "..."}. The message field is human-readable; do not match programmatically on it.

Programmatic matching example

const settle = await fetch(facilitatorUrl + "/settle", { ... }).then(r => r.json());

if (settle.success) {
  // happy path
} else {
  switch (settle.errorCode) {
    case "in_flight":
      return retryAfter(...);
    case "user_op_failed":
      return surfaceRevert(settle.message);
    case "bundler_error":
    case "receipt_timeout":
      return retryWithBackoff(...);
    case "replay":
      // already settled — no further action; record the original txHash if possible
      break;
    default:
      // unknown codes are reserved and may be added in future versions
      log.warn("unknown settle errorCode", settle.errorCode, settle.message);
  }
}

On this page