Overview

Errors

One error envelope, eighteen typed codes, and which of them are worth retrying.

HTTP, MCP and A2A expose typed gateway errors in different forms. HTTP returns this envelope. MCP puts it in structuredContent, sets isError: true, repeats it as JSON in content[0].text and adds a CODE: message line as content[1].text. A2A puts the error in a failed task's artifact. ACP returns its own error document.

Error envelopejson
{
  "status": "error",
  "code": "INPUT_INVALID",
  "message": "…",
  "retryable": false,
  "requestId": "…",
  "resourceId": "weather_basic"
}
status"error"required
codeCommerceErrorCoderequired
One of the codes below. Stable — safe to branch on.
messagestringrequired
Human-readable. Stack traces and internal messages never cross the boundary.
retryablebooleanrequired
Whether the same request may reasonably be retried.
requestId / resourceIdstring
Correlation identifiers, when known.
detailsobject
Client-safe extra context. After a settled payment whose delivery failed, it carries the payment summary. When the pipeline offers a retry, as after a rejected proof or a refused settlement, details.challenge carries the rail's fresh challenge.

Codes

CodeHTTPRetryMeaning
CONFIG_INVALID500nothe configuration is invalid
RESOURCE_NOT_FOUND404nono such resource, or not exposed on this surface
INPUT_INVALID400noinput failed the resource's schema, or a path parameter was empty, . or ..
PAYMENT_REQUIRED402noa paid resource was called without a proof
PAYMENT_INVALID402 / 412nomalformed, wrong amount, recipient, network, asset, expired or underfunded; 412 over HTTP for a missing Permit2 allowance
PAYMENT_REPLAYED409 / 402no409 while the first attempt is unfinished or uncertain; 402 with a fresh challenge after settlement or an MPP rejection. A rejected x402 attempt accepts the same proof again
PAYMENT_PROVIDER_UNAVAILABLE503yesthe payment rail, facilitator or RPC gave no verdict; HTTP includes Retry-After: 5
PAYMENT_SETTLEMENT_FAILED502 / 402no502 when settlement cannot be confirmed; 402 when the facilitator refuses it
AUTHORIZATION_REQUIRED403nothe resource requires an AP2 mandate
AUTHORIZATION_INVALID403nothe mandate failed verification
AUTHORIZATION_REPLAYED409nothe mandate was already spent
AUTHORIZATION_PROVIDER_UNAVAILABLE503yesthe verifier or its store failed; HTTP includes Retry-After: 5
BACKEND_TIMEOUT504yesthe merchant backend did not answer in time
BACKEND_ERROR502nothe backend failed, redirected, or answered too much
PROTOCOL_UNSUPPORTED501nothe operation is not implemented
GATEWAY_BUSY503yesload shedding, or a full A2A pending-payment store; back off and retry
STORAGE_ERROR500nothe receipt store failed
INTERNAL_ERROR500noanything else; no internals are disclosed

Two refusals come before any route handler and use codes outside this list: the Host and Origin check answers 403 with FORBIDDEN, and the admin token gate answers 401 with UNAUTHORIZED. Their body has only status, code and message.

Framework errors use statusCode, code, error and message: malformed JSON returns 400 FST_ERR_CTP_INVALID_JSON_BODY, unsupported content types 415 FST_ERR_CTP_INVALID_MEDIA_TYPE, and oversized bodies 413 FST_ERR_CTP_BODY_TOO_LARGE. Unknown routes return 404 with message, error and statusCode. Operator routes return an empty 404 when server.adminToken is unset.

Why authorization failures are 403, not 402A 402 tells a client “pay and retry”, which cannot fix a missing or rejected mandate — and a client that auto-pays on 402 would be charged for a request that was never going to be delivered.

Payment required is not an error

An unpaid call to a paid resource returns 402 with a status: "payment-required" envelope instead — see Invoke a resource.