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.
{
"status": "error",
"code": "INPUT_INVALID",
"message": "…",
"retryable": false,
"requestId": "…",
"resourceId": "weather_basic"
}status"error"requiredcodeCommerceErrorCoderequiredmessagestringrequiredretryablebooleanrequiredrequestId / resourceIdstringdetailsobjectdetails.challenge carries the rail's fresh challenge.Codes
| Code | HTTP | Retry | Meaning |
|---|---|---|---|
CONFIG_INVALID | 500 | no | the configuration is invalid |
RESOURCE_NOT_FOUND | 404 | no | no such resource, or not exposed on this surface |
INPUT_INVALID | 400 | no | input failed the resource's schema, or a path parameter was empty, . or .. |
PAYMENT_REQUIRED | 402 | no | a paid resource was called without a proof |
PAYMENT_INVALID | 402 / 412 | no | malformed, wrong amount, recipient, network, asset, expired or underfunded; 412 over HTTP for a missing Permit2 allowance |
PAYMENT_REPLAYED | 409 / 402 | no | 409 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_UNAVAILABLE | 503 | yes | the payment rail, facilitator or RPC gave no verdict; HTTP includes Retry-After: 5 |
PAYMENT_SETTLEMENT_FAILED | 502 / 402 | no | 502 when settlement cannot be confirmed; 402 when the facilitator refuses it |
AUTHORIZATION_REQUIRED | 403 | no | the resource requires an AP2 mandate |
AUTHORIZATION_INVALID | 403 | no | the mandate failed verification |
AUTHORIZATION_REPLAYED | 409 | no | the mandate was already spent |
AUTHORIZATION_PROVIDER_UNAVAILABLE | 503 | yes | the verifier or its store failed; HTTP includes Retry-After: 5 |
BACKEND_TIMEOUT | 504 | yes | the merchant backend did not answer in time |
BACKEND_ERROR | 502 | no | the backend failed, redirected, or answered too much |
PROTOCOL_UNSUPPORTED | 501 | no | the operation is not implemented |
GATEWAY_BUSY | 503 | yes | load shedding, or a full A2A pending-payment store; back off and retry |
STORAGE_ERROR | 500 | no | the receipt store failed |
INTERNAL_ERROR | 500 | no | anything 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.
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.