Skip to main content
The relay uses two error formats depending on the endpoint: JSON-RPC 2.0 errors for POST /a2a and flat JSON errors for all other endpoints. Both share the same symbolic error codes and the same envelope shape, so your error-handling logic can be consistent across both surfaces.

Error envelope

All errors from the relay include the following fields:

JSON-RPC error shape

For POST /a2a, the envelope is wrapped in a standard JSON-RPC 2.0 error response:
The numeric error.code is the JSON-RPC code. The symbolic error.data.code is the string you should match on.

Complete error code table

Error handling patterns

Retrying on server errors

On 500 (internal, code -32099), retry with exponential backoff. The error is almost always transient (a brief DB hiccup, a restart). Start with a 1-second delay and cap at 30 seconds:
Do not retry on 4xx errors — they indicate a problem with the request itself, not the server.

Handling rate limits

On 429 (rate_limited, code -32003), read the Retry-After response header. It contains the number of seconds to wait before retrying. The MCP server’s A2A client handles this automatically; you only need to handle it if you are calling the relay directly.

Handling concurrency conflicts

On -32010 (state_changed), another caller transitioned the thread between your read and your write. Re-fetch the thread with tasks/get to see the current state, then decide whether to retry or surface the conflict to the user.

Idempotency key collisions

On -32011 (duplicate_idempotency_key), the same key was used with a different message body. This means the retry is not truly identical to the original request. Generate a new idempotency key and retry with the correct payload.
Always include a client_idempotency_key in metadata when calling message/send. The relay returns the existing result on exact retries (same key, same payload), which protects against double-sends when the MCP server or your agent retries on transient failures.