# Errors

URL: https://docs.router.ezat.io/reference/errors

> Every HTTP status and error type the gateway returns in the Anthropic and OpenAI formats, what causes each and whether to retry, errors during a stream, and the request ids to send to support.

## When to use this page

A request failed and you want to know why, whether to retry it, or what to send to support. Limits and backoff are on [Rate limits](https://docs.router.ezat.io/reference/rate-limits.md).

## Error format

Errors use the format of the API you called, with the same HTTP status on both.

`/v1/*` (Anthropic format):

```json
{
  "type": "error",
  "error": {
    "type": "not_found_error",
    "message": "model: anthropic/no-such-model"
  },
  "request_id": "0b1c6f2e-5d4a-4f7e-9a51-3f0e8d2c7b19"
}
```

`/openai/v1/*` (OpenAI format):

```json
{
  "error": {
    "message": "The model `anthropic/no-such-model` does not exist or you do not have access to it.",
    "type": "invalid_request_error",
    "param": "model",
    "code": "model_not_found"
  }
}
```

The Anthropic and OpenAI SDKs turn these into their own error classes (for example `NotFoundError`), so you rarely parse them yourself. See the error handling sample in [curl](https://docs.router.ezat.io/examples/curl.md), [Node.js](https://docs.router.ezat.io/examples/node.md) or [Python](https://docs.router.ezat.io/examples/python.md).

## Status codes

| Status | Anthropic `type` | OpenAI `type` / `code` | Cause | Retry? |
| --- | --- | --- | --- | --- |
| 400 | `invalid_request_error` | `invalid_request_error` / `null` | The body isn't valid JSON, a required field is missing or wrong (`model`, `max_tokens`, `messages`), the model refused a parameter, or the OpenAI-compatible API doesn't support a field you sent | No. Fix the request. |
| 401 | `authentication_error` | `invalid_request_error` / `invalid_api_key` | The key is missing, wrong, revoked or expired | No. Check the key. |
| 402 | `billing_error` | `insufficient_quota` / `insufficient_quota` | The organization's balance is zero or below, or the key reached its spend limit | No. Add credit or raise the limit. |
| 403 | `permission_error` | `permission_error` / `null` | The key isn't allowed to use this model, or the organization is suspended | No |
| 404 | `not_found_error` | `invalid_request_error` / `model_not_found` | The model is unknown, retired, or in preview and the key has no preview access | No. Use a model from [Models](https://docs.router.ezat.io/models.md). |
| 413 | `request_too_large` | `invalid_request_error` / `request_too_large` | The request body is over 32 MB | No. Send less. |
| 429 | `rate_limit_error` | `rate_limit_error` / `rate_limit_exceeded` | The key went over its requests per minute, or the providers are rate limiting this model | Yes, after `retry-after` |
| 500 | `api_error` | `server_error` / `null` | An unexpected error in the gateway or at the provider | Yes, a few times with backoff |
| 504 | `timeout_error` | `server_error` / `timeout` | A non-streaming request took longer than 590 seconds | Send it again with `"stream": true` |
| 529 | `overloaded_error` | 503, `server_error` / `overloaded` | No capacity for this model right now, or every provider the gateway tried failed | Yes, after `retry-after` |

The OpenAI-compatible API returns 503 where the Anthropic format returns 529, because OpenAI clients don't know 529. Its 404 for a model also sets `"param": "model"`.

None of these errors is charged. See [How billing works](https://docs.router.ezat.io/billing.md).

### Messages you may see

| Status | `message` |
| --- | --- |
| 400 | `Request body is not valid JSON`, or `<field>: <problem>`, such as `max_tokens: …` or `n: only 1 choice per request is supported` |
| 401 | `Missing API key. Send it in the x-api-key or Authorization: Bearer header.` |
| 401 | `Invalid API key`, `This API key has been revoked`, `This API key has expired` |
| 402 | `Your credit balance is too low. Add credits in the EZAT Router dashboard.` |
| 402 | `This API key reached its daily spend limit of …` (or `monthly`, `total`) |
| 403 | `This API key is not allowed to use …` |
| 403 | `This organization is suspended. Contact support@ezat.io.` |
| 404 | `model: <the name you sent>` |
| 429 | `This API key exceeded its requests-per-minute limit` (`retry-after: 30`) |
| 429 | `Rate limited upstream. Please retry shortly.` (`retry-after: 10`) |
| 504 | `The model took longer than 590 seconds. Send long requests with "stream": true.` |
| 529 | `Overloaded`, or `… has no available capacity right now` (`retry-after: 5`) |

A 400 that the model itself returns (for example a parameter the model doesn't accept) carries the model's own message.

### Paths that don't exist

A path the gateway doesn't serve, such as `/v1/messages/batches` or `/v1/files`, returns 404 in a plain format, not in either envelope:

```json
{ "statusCode": 404, "message": "Cannot POST /v1/messages/batches" }
```

## Errors during a stream

Once a streamed response has started, the HTTP status is already 200. If something fails after that, the gateway sends one error event and ends the stream. The request is marked **Failed** in Usage and isn't charged.

Anthropic format: an `error` event, with no `message_stop` after it.

```text
event: error
data: {"type":"error","error":{"type":"api_error","message":"Upstream provider error"}}
```

OpenAI format: a chunk with an `error` object, then `[DONE]`.

```text
data: {"error":{"message":"Upstream provider error","type":"server_error","param":null,"code":null}}

data: [DONE]
```

The error types are the same as in the table above. The Anthropic and OpenAI SDKs raise an `APIError` (or a subclass) when they read it, and don't retry it: send the request again yourself if you want to.

If your client disconnects mid-stream, there is no error event; see [interrupted streams](https://docs.router.ezat.io/billing.md).

## Request ids

Two response headers identify a request:

| Header | On | Value |
| --- | --- | --- |
| `request-id` | Messages and chat completions that passed the gateway's checks and reached a model, successful or not, streamed or not | The id of the request in your Usage |
| `x-request-id` | Every response, including refused requests | An id for this HTTP call. If you send your own `x-request-id` (letters, digits, `.`, `_`, `:`, `-`, up to 128 characters), it is used and sent back. |

- In the Anthropic format, the error body's `request_id` is the `x-request-id`.
- On the OpenAI-compatible API, a completion's `id` is `chatcmpl-` followed by its `request-id`.
- The Anthropic SDKs expose `request-id` (`message._request_id`, and `error.request_id` in Python or `error.requestID` in Node). For a refused request there is no `request-id`, so read `x-request-id` from the error's response headers.
- The OpenAI SDKs expose `x-request-id` (`completion._request_id`, `error.request_id` in Python, `error.requestID` in Node).

## Contact support

Email support@ezat.io with:

1. The `request-id`, if the response had one, and the `x-request-id`.
2. The time of the request (with its time zone), the model, and which API you called (`/v1/messages` or `/openai/v1/chat/completions`).
3. The HTTP status and the error `message`.

Never send your API key. Request and response bodies aren't stored, so include the parts of the request that matter.

## For AI agents

1. Read the HTTP status first, then `error.type` (Anthropic format) or `error.code` (OpenAI format).
2. Don't retry 400, 401, 402, 403, 404 or 413. Fix the request, tell the user to check the key or add credit at https://router.ezat.io/billing, or pick a model from `GET https://api.router.ezat.io/v1/models`.
3. Retry 429, 500, 529 and 503 with exponential backoff, waiting at least the `retry-after` seconds when the header is there. The official Anthropic and OpenAI SDKs already do this, twice by default.
4. For a 504 `timeout_error`, resend with `"stream": true` instead of retrying as is.
5. A stream can end with an `error` event (Anthropic) or an `error` chunk (OpenAI) after a 200. Treat it like a 500.
6. When reporting a problem, give the user the `request-id` and `x-request-id` headers and the time. Never include the API key.