# Errors

> HTTP status codes, the OpenAI error envelope, Anthropic error shapes, request IDs, sanitization, and retry guidance.

## Status codes

| Code | Meaning | Retry? |
|---|---|---|
| 401 | Missing or invalid API key | No — fix credentials |
| 403 | Valid key, request rejected (balance, whitelist, quota, IP) | After resolving the cause |
| 413 | Request body too large | Reduce size and retry |
| 429 | Rate limited | Yes — exponential backoff, no `Retry-After` |
| 500 | Gateway or upstream failure | Yes — safe to retry idempotent requests |

Insufficient balance is rejected with 403.

## Response envelopes

### OpenAI-compatible endpoints

Errors use the OpenAI envelope:

```json
{
  "error": {
    "message": "…",
    "type": "new_api_error",
    "param": null,
    "code": null
  }
}
```

`error.type` distinguishes the failure source:

- `new_api_error` — the MirAPI gateway rejected the request (auth, balance, quota, validation).
- `upstream_error` — the upstream model provider failed; the message is sanitized so vendor internals never leak.

### Anthropic (`/v1/messages`)

The same failure uses the Anthropic error shape:

```json
{
  "type": "error",
  "error": {
    "type": "permission_error",
    "message": "…"
  }
}
```

## Request IDs

Every error message ends with a request ID suffix so you can find the exact call in the logs (console, `GET /api/usage/token`, `GET /api/log/token`). Include it when opening a billing or support ticket.

## Sanitization

Upstream responses are sanitized before reaching you: provider-internal details (hosts, tokens, stack traces, model IDs) are stripped and replaced with a stable message. Log the sanitized message and don't guess at the underlying provider.

## Retry guidance

- MirAPI does **not** retry on your behalf.
- 429 responses carry **no `Retry-After`** header — use exponential backoff with jitter (1s → 2s → 4s, capped at ~30s).
- 500s are safe to retry for idempotent requests; do not blindly replay non-idempotent ones (generation, tool side effects).