> ## Documentation Index
> Fetch the complete documentation index at: https://docs.codex.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Errors & Retries

> How Codex reports errors, which ones are safe to retry, and how long to wait

## How Errors Arrive

Codex is a GraphQL API, so most errors come back in the `errors` array of an otherwise normal response — **HTTP 200 with an `errors` key**, not an HTTP error status. Authentication, payment (402), rate limiting and infrastructure failures (502, 503, 504, and an HTML 403 from the edge firewall) fail at the HTTP layer instead; see the table below.

```json theme={null}
{
  "errors": [
    {
      "message": "The requested resources are over capacity. Retry in about 30 seconds — retrying sooner will be throttled.",
      "extensions": {
        "code": "OVER_CAPACITY",
        "retryAfterSeconds": 30
      }
    }
  ]
}
```

<Warning>
  If your error handling only inspects HTTP status codes, it will treat these as successful responses. Always check whether the body contains an `errors` array.
</Warning>

## Which Errors to Retry

There is one field to watch: **if an error carries `extensions.retryAfterSeconds`, wait that many seconds before retrying that request.** On rate-limited responses the same value is also sent as the standard `Retry-After` header, so most HTTP client libraries will honor it without any code from you.

| Code | HTTP status | Meaning | Retry |
| - | - | - | - |
| `TOO_MANY_REQUESTS` | 429 | You exceeded your plan's rate limit | After `retryAfterSeconds`, or your own backoff if absent |
| `OVER_CAPACITY` | 200 | We're briefly over capacity and scaling up | After `retryAfterSeconds` |
| `UNAUTHENTICATED` | 401 | Unknown or invalid API key | No, fix the API key |
| (none) | 402 | No `Authorization` header: the request is treated as a pay-per-query [MPP](/agents/mpp) call and returns `application/problem+json` | No, send your API key |
| `FORBIDDEN` | 403 | Your key is valid but the account or key is not allowed to make this request — see [Forbidden (403)](#forbidden-403) | No — fix the account state first |
| (none, HTML page) | 403 `Request blocked` | The edge firewall blocked the client IP before the request reached the API (unlike the JSON `FORBIDDEN` 403) | Back off; send the CloudFront request ID and your egress IP to support |
| `ROUTER_UNAVAILABLE` | 503 | Brief server-side outage | Yes, after `Retry-After` (2 seconds) |
| (none) | 502, 504 | The load balancer could not reach the API; the response has no GraphQL body | Yes, with backoff; include the request ID if it repeats |
| Everything else | 200 | Unexpected failure on our side | Not automatically — see below |

### Rate limited

A `TOO_MANY_REQUESTS` error means you exceeded your plan's per-second rate limit. When we can tell you exactly when the limit lifts, we do, via `retryAfterSeconds` and the `Retry-After` header.

A 429 **without** `retryAfterSeconds` means your request budget is momentarily empty rather than your account being throttled — capacity typically returns within a second. Back off on your own schedule and retry; don't retry immediately in a tight loop.

See [Rate Limits & Connection Limits](/concepts/rate-limits) for the limits themselves.

### Over capacity

An `OVER_CAPACITY` error means the data your query needs is temporarily under more load than it can serve, and we're scaling up. It is not caused by anything wrong with your request — the same query will succeed once you retry.

These arrive as HTTP 200 with the error in the body. Retry after `retryAfterSeconds` (currently 30). Retrying sooner is likely to be throttled and slows the recovery for everyone.

### Forbidden (403)

A `FORBIDDEN` error means the API key was recognized but the request is not allowed right now. The message says why:

| Message | Meaning | What to do |
| - | - | - |
| `Your account has exceeded its usage limit for this billing period, please upgrade your plan` | The account has used its whole allowance for the period and is paused until the period rolls over. | Upgrade your plan, or wait for the next period. |
| `Your API key is not activated.` | The key has been deactivated. | Reactivate it or create a new one in the [dashboard](https://dashboard.codex.io/dashboard?utm_source=codex\&utm_medium=docs\&utm_campaign=concepts-errors). |
| `You've exceeded the maximum number of connections, please upgrade your plan` | The key has reached its WebSocket connection limit. | Close connections you no longer need, or upgrade. See [Rate Limits & Connection Limits](/concepts/rate-limits#websocket-connection-limits). |
| `Websockets are not enabled for your account, please upgrade your plan` | Your plan does not include subscriptions, or WebSockets are not enabled on this API key. | Upgrade, or contact support if your plan includes them. |
| `Your account has violated terms of service, please contact support` | The account has been suspended. | [Contact support](mailto:support@codex.io). |

Over WebSocket, the same conditions close the connection with code `4403` and the message as the close reason. An account that becomes paused while a subscription is running is disconnected within about a minute with the reason `Forbidden`.

### Everything else

Unexpected errors return a generic message with an error code:

```json theme={null}
{
  "errors": [
    {
      "message": "Something went wrong. Error Code: 1a2b3c4d5e6f7890"
    }
  ]
}
```

Don't retry these automatically — the same request will usually fail the same way. Include that error code when you [contact support](mailto:support@codex.io) or ask on [Discord](https://discord.gg/9ZB7zcWuBY); it lets us find the exact failure in our logs.

## Retry Strategy

<Info>
  Fixed retry intervals are the most common cause of prolonged rate limiting. If your interval is shorter than the penalty window, every retry lands inside it and extends the problem.
</Info>

A retry policy that works well against Codex:

1. **Honor `retryAfterSeconds` whenever it's present.** It's computed from the actual condition — it isn't a guess, and it overrides whatever interval you'd otherwise use.
2. **Otherwise use exponential backoff with jitter.** Jitter matters if you run multiple workers: without it they synchronize and retry in a thundering herd.
3. **Cap your retries.** Three to five attempts is plenty; past that the condition needs attention rather than another request.
4. **Don't retry non-retriable errors.** Auth failures and malformed queries will fail identically every time.

If you're hitting rate limits often enough that retry behavior matters, [Optimization](/concepts/optimization) covers how to reduce request volume — usually the better fix.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.