Errors in MCP
What MCP error envelopes look like and how an agent should react.
Where errors show up
MCP responses come back as JSON-RPC result objects with a content array. When a tool call fails, the result still includes structured error info: usually as a JSON object inside a text content block.
{
"content": [
{
"type": "text",
"text": "{\"error\":{\"type\":\"invalid_request_error\",\"code\":\"resource_not_found\",\"message\":\"Customer cus_xxx not found.\",\"request_id\":\"req_...\"}}"
}
],
"isError": true
}
The inner error object follows EPD’s standard REST error shape: same fields, same codes. Read REST errors for the full reference; everything there applies here.
Common error codes by category
| Category | Codes |
|---|---|
| Authentication | missing_api_key, invalid_api_key, expired_api_key, revoked_api_key |
| Authorization | insufficient_permissions, ip_not_allowed, environment_mismatch |
| Validation | validation_error, required_field, invalid_value, invalid_format |
| State | resource_not_found, invalid_state_transition, already_canceled |
| Idempotency | request_in_progress, idempotency_key_conflict |
| Rate limiting | rate_limit_exceeded, global_rate_limit_exceeded |
| Payment | gateway_error, customer_not_configured, merchant_not_configured |
| MCP-specific | not_implemented (e.g. pause_subscription returns this until the feature ships) |
A declined charge is not an error. create_order, create_subscription, and a charge retry (retry_order / retry_failed_charge) return a status: "failed" order carrying a normalized failure_code (for example processor_declined, insufficient_funds, expired_card, incorrect_cvv). Branch on failure_code, not on an error code. (retry_order also retries a failed refund, which comes back as a refund_failed order rather than a charge failure.) The full set is in Decline reason codes.
How an agent should react
validation_error and friends mean the tool call was malformed. Return the message + field_errors to the user; do not retry.
invalid_api_key, insufficient_permissions, ip_not_allowed are configuration problems. Stop and notify the operator; retrying will not fix them.
rate_limit_exceeded or global_rate_limit_exceeded: pause for a few seconds with jitter, then retry the same call.
gateway_error, internal_error, service_unavailable are transient. Retry with the same idempotency_key.
A declined charge comes back as a failed order with a failure_code (processor_declined, insufficient_funds, expired_card, and so on). It needs a human decision: collect a different card or wait. Do not silently retry.
Where to find request_id
Every error includes a request_id. When opening a support ticket about an MCP call, paste it; it lets support trace your exact call instantly.
"request_id": "req_a1b2c3d4e5f67890abcdef0123456789"