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

# Predictable failures.

> The API returns standard HTTP status codes with a JSON error body shaped for the protocol you called — Anthropic-shaped on /v1/messages, OpenAI-shaped on the OpenAI endpoints. Match on the status; read error.message for the human explanation.

## Status codes

Match on the status first, then on `error.code` (also sent as the `x-sideren-error-code`
header) when you need the exact reason.

| Status | Codes | Meaning |
| - | - | - |
| `400` | `invalid_json` · `invalid_request` · `context_length_exceeded` · `content_filter` | Malformed JSON, a bad parameter (`error.param` names it), a prompt longer than the model's context window, or a request the provider's safety system blocked. Fix the request; retrying the same body fails the same way. |
| `400` | `plan_upgrade_required` · `payg_required` · `input_exceeds_plan` · `verification_required` | The key is fine but the plan doesn't cover this request. `plan_upgrade_required`: the model isn't in your plan; the message names the cheapest plan that includes it and, on Free, the models you can use. `payg_required`: the model runs only in [PAYG mode](/plans#pay-as-you-go); turn on PAYG mode for the key or pick another model. `input_exceeds_plan`: a Free prompt over the Free limit. Never retryable. |
| `401` | `invalid_api_key` · `missing_api_key` · `revoked_api_key` | Missing, malformed or revoked key. Check the header for the protocol you're calling. |
| `402` | `allowance_exhausted` | Your plan's monthly credits are used. The message says how much you have used, when the credits renew and which plan has more. Upgrade, or use purchased credit in PAYG mode. No `retry-after`: see [Past your limits](/plans#past-your-limits). |
| `402` | `credit_used_up` · `token_allowance_exhausted` | Free's 200 credits are used, or the Free plan's usage limit is reached. Subscribe, or add credit and turn on PAYG mode for your key. |
| `402` | `paid_credit_required` · `insufficient_paid_credit` · `subscription_required` · `key_budget_exhausted` | PAYG mode with no purchased credit (or not enough for this request), a PAYG-only account sending `x-sideren-payment-mode: subscription`, or a key that reached the monthly spending cap you set for it. Top up, change the mode, or raise the key's cap in the dashboard. |
| `403` | `account_suspended` · `account_closed` · `permission_denied` | The account or key is not allowed to make requests. Never retryable. |
| `404` | `not_found` · `unknown_model` · `model_unavailable` | Unknown route or model id: check the path and the catalog (`GET /v1/models`). `model_unavailable` lists models you can use instead. An id another customer's key owns (a batch, a stored response) is also a `404`, never their data. |
| `405` | `method_not_allowed` | Right path, wrong method. Carries an `Allow` header. |
| `409` | `idempotency_in_progress` | A request with the same `Idempotency-Key` is still running. Batch results requested before the batch `ended` also return `409`. See [Idempotency](/api/idempotency). |
| `410` | `endpoint_retired` | A retired endpoint, or a batch whose results have expired (batches and their results are kept for 24 hours). |
| `413` | `body_too_large` | The request body is too large (10 MB). Trim the payload. |
| `422` | `route_not_qualified` · `request_unprocessable` | The request needs a feature the chosen model can't provide, or it can't be served within the admitted limits. Switch models or drop the requirement. |
| `429` | `rate_limit_exceeded` · `concurrency_limit_exceeded` · `tokens_per_minute_exceeded` · `network_rate_limited` | Your plan's request window, concurrency or tokens-per-minute limit, or the Free plan's per-network pace. Carries `retry-after` (seconds): back off and retry. |
| `500` | `internal_error` | Internal error. Transient: retry with backoff. |
| `503` | `capacity_unavailable` · `upstream_overloaded` · `upstream_unavailable` | Capacity for the model is briefly short, or the model returned an error. Carries `retry-after`; retry shortly. On `/v1/messages` a capacity shortage is `529 overloaded_error`, as Anthropic's API sends it. |
| `504` | `upstream_timeout` | The model did not answer in time. Retry with backoff. |

The error `type` follows the protocol you called: `invalid_request_error`,
`authentication_error`, `billing_error` (402), `permission_error` (403),
`rate_limit_error` (429), and `api_error` or `server_error` for 5xx (`overloaded_error` for a
capacity shortage on `/v1/messages`).

## Error body shape

Every error returns a JSON body with an `error` object carrying a `type` and a `message`.
On the Anthropic endpoint it is Anthropic-shaped:

```json title="Anthropic-shaped (/v1/messages)" theme={null}
{
  "type": "error",
  "error": {
    "type": "authentication_error",
    "code": "invalid_api_key",
    "message": "Invalid API key."
  }
}
```

On the OpenAI endpoints it is OpenAI-shaped:

```json title="OpenAI-shaped (/v1/chat/completions, /v1/responses, ...)" theme={null}
{
  "error": {
    "type": "authentication_error",
    "code": "invalid_api_key",
    "message": "Invalid API key.",
    "param": null
  }
}
```

## Retries and backoff

The retry recipe every production agent should ship:

* **Retry only what can succeed**: `429` and `5xx`. A `402` (credits used up) or `403`
  won't clear by waiting, and any other `4xx` will fail identically on every attempt; fix
  the request instead.
* **Honor `retry-after` exactly**: on `429` and `503` it is the number of seconds until
  there is room. Waiting less just burns attempts.
* **Exponential backoff with jitter** everywhere else — e.g. 1s, 2s, 4s, 8s with ±20%
  jitter, capped around five attempts.
* **Fail loud after the cap** — surface the final `error.message`; it is written to be
  actionable.

```python title="Minimal backoff loop" theme={null}
import random, time

def with_retries(send, max_attempts=5):
    for attempt in range(max_attempts):
        r = send()
        if r.status_code < 400:
            return r
        if r.status_code == 429:
            time.sleep(int(r.headers.get("retry-after", "1")))
        elif r.status_code >= 500:
            time.sleep((2 ** attempt) * (1 + random.uniform(-0.2, 0.2)))
        else:
            break  # other 4xx: not retryable
    raise RuntimeError(r.json()["error"]["message"])
```

<Note>
  The official OpenAI and Anthropic SDKs already do all of this — including honoring
  `retry-after` — with no configuration. Hand-rolled HTTP clients are where retries usually
  go missing.
</Note>

## Streaming failures

If a stream cannot complete, it ends with a protocol-shaped error event rather than
silently returning an empty response — your client always learns the turn failed. Treat a
mid-stream error like a `5xx`: retry the whole request with backoff. Because the model may
already have emitted tool calls your client ran, decide in your client whether to send the
turn again. See [Streaming](/guides/streaming).


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