# Errors and retries

> The Markdown form of https://openfill.ai/docs/errors. The index of every docs page is https://openfill.ai/llms.txt, and every page in one file is https://openfill.ai/llms-full.txt.

A failed order is charged nothing, with one exception the 500 row names: a response that was produced and delivered but could not be completed. The codes below distinguish a problem you can fix from one you should retry, and each row says what was charged.

## The error envelope

Chat, completions and Responses HTTP failures use the OpenAI error shape. Branch on `code` where it is set, and on `type` otherwise: the table below names which field carries the identifier for each status. Where a `Retry-After` header is sent it is in whole seconds. Every response carries an `x-request-id` header, which the OpenAI SDKs expose as `_request_id`; quote it when you write to us about a failure and we can find that exact request in the gateway log.

```json
{
  "error": {
    "message": "no price limit set for model '<model_id>': every request needs one, and it is set per model. Pass price_limit (extra_body with the OpenAI SDK) or an X-Price-Limit header, or set a default for this model on the Bidding page (per key, or per account).",
    "type": "invalid_request_error",
    "param": null,
    "code": "missing_price_limit"
  }
}
```

## Status codes

| Status | Identifier | Meaning | Charged | Retry |
| --- | --- | --- | --- | --- |
| 400 | - code `missing_price_limit`<br>- code `invalid_output_cap`<br>- code `model_deprecated`<br>- code `model_preview`<br>- code `fixed_price_model`<br>- code `unknown_model`<br>- code `image_url_unfetchable`<br>- type `invalid_request_error` | Malformed request, no price set anywhere, a retired model, or a request the model itself refused. model_preview: a request to a preview of the market, which opens soon; its prices are simulated until then, and the fixed-price models run today. fixed_price_model: a market endpoint asked about a fixed-price model, or a price limit set for one on the account or a key. unknown_model: a model filter naming no served model. image_url_unfetchable: an image URL that could not be fetched, with the part it sits in and the reason in the message | No | No, fix the request |
| 401 | - type `authentication_error` | Missing, invalid or revoked API key | No | No |
| 402 | - code `insufficient_balance`<br>- type `insufficient_quota` | Your available balance, after holds for orders already executing, cannot cover this request at the current clearing level, capped by the price you set, or at a fixed-price model's prices. A level of 0 needs no balance | No | After topping up, or when the level falls on a market model |
| 403 | - type `email_unverified`<br>- type `authentication_error` | Your account is not active: unverified, suspended, or deletion requested | No | No |
| 404 | - code `model_not_found`<br>- code `not_found`<br>- type `invalid_request_error` | The model is not available (model_not_found); no such order, file or batch (code null); or no such route (not_found). GET /v1/models lists the models served | No | No, fix the request |
| 408 | - type `order_expired` | An unlocked order on a market model waited out its timeout (max_wait, else your account's order timeout) without starting | No | Yes, with a higher price or a longer max_wait |
| 409 | - type `order_not_cancelable`<br>- type `order_canceled`<br>- type `idempotency_error`<br>- type `batch_not_cancelable`<br>- type `result_write_pending`<br>- type `batch_in_progress`<br>- type `file_in_use` | The order is executing or already finished, so it cannot be cancelled; the original of an idempotent replay was cancelled; a result cannot be deleted yet because its write or its batch has not finished; or a file cannot be deleted while a batch is still working from it | No | No, except a result whose write has not landed, which carries Retry-After |
| 410 | - type `result_unavailable` | The result is gone: nothing was stored (storage is off by default), its storage period has passed, or you deleted it | As the original was | No. Use a new Idempotency-Key to execute again |
| 413 | - type `invalid_request_error` | Request body over the size limit (8 MB) | No | No, split the request |
| 429 | - code `capacity_unavailable`<br>- code `result_pending`<br>- type `rate_limit_error` | A rate limit or queue cap, a missed lock window (10 seconds), a request to a fixed-price model that did not start before its timeout, or result_pending while an already completed result is being stored | No new charge; result_pending keeps the original charge | Yes, after Retry-After where one is sent. SDKs do this |
| 500 | - type `server_error` | An error on our side. Where the response was already produced and charged the message says so | Recorded or uncertain; inspect the message and usage | Yes, unless the response was produced |
| 502 | - type `server_error` | The backend failed after part of a stream had been delivered. This one arrives inside the stream alone; no HTTP response carries it | No | Yes |
| 503 | - code `image_fetch_unavailable`<br>- type `server_error` | Backend unavailable, a deploy in progress, unavailable object storage, or a stored result that could not be read. image_fetch_unavailable: image URLs cannot be fetched right now, and the same image sent as a base64 data URL is accepted | No new charge; a failed read keeps the original charge | Yes, after Retry-After where one is sent |

Two `429`s mean different things. `rate_limit_error` is a ceiling on [rate limits](https://openfill.ai/docs/rate-limits) and clears with time. `capacity_unavailable` is a price-locked request that did not start inside its execute window, or a request to a fixed-price model that did not start before its timeout. A locked retry gets a fresh window at the standing quote, whose interval is published on the quote endpoint, and is never charged more than that quote; a fixed-price retry waits again at the model's prices.

## The envelope on /v1/messages

`POST /v1/messages` answers every error in the Anthropic shape instead, `{type: error, error: {type, message, code}}`, with the same message and the same code as the rows above and the type from Anthropic's vocabulary. The [Messages API page](https://openfill.ai/docs/api/messages#errors) maps the two.

## Retrying

| Do | Statuses | How |
| --- | --- | --- |
| Retry with backoff | 429, 500, 503, and a 502 inside a stream | Honour Retry-After where it is sent. The OpenAI SDKs retry 408, 409, 429 and every 5xx on their own with exponential backoff, honouring Retry-After; an in-band error is yours to retry. |
| Retry after a change | 402, 408 | Add credits, or wait for the level to fall; raise the price limit or the timeout. |
| Do not retry | 400, 401, 403, 404, 409, 410, 413 | The same request gets the same answer. Fix it, or use a new Idempotency-Key where the row says so. |

A retry of an unlocked request re-queues it with a fresh deadline, so an SDK left on its default retries can wait several timeouts in a row. Set `max_retries` when you want a hard cutoff, and carry an `Idempotency-Key` so a retry can never run a successful request twice while that key remains valid.

### Errors on a streaming request

A streaming request is answered `200` once its order is queued, and every failure after that arrives inside the stream in the wire's error event, with no status code and no `Retry-After`, so SDK retries do not engage. The [streaming page](https://openfill.ai/docs/streaming#errors) has the frames.

## Idempotency

Send an `Idempotency-Key` header of up to 255 characters on a chat or completion, Responses or Messages request to make retries safe. The key is scoped to your account, and a replay returns the original order's outcome rather than running it again. The work runs once and is charged once.

| The original order is | A replay gets |
| --- | --- |
| Still queued or executing | The same response, when it arrives. Chat and completions replay one chunk; Responses and Messages replay their event sequences. The replay waits without opening a stream, so its caller timeout still applies. |
| Done, with the result stored | The stored response, with the same usage and x_market. |
| Done, with nothing stored | 410 result_unavailable. Data storage is off by default, so this is the default after completion; use a new key to execute again. |
| Done, but its result is still being written | 429 result_pending with Retry-After. Retry with the same key. |
| Expired, failed or cancelled | The key is released, so the same key can run a new order. A replay arriving before release sees the original outcome; a persisted error can replay as 503. |
| Sent with different execution fields or price and wait headers | 409 idempotency_error. A key names one translated request. Native wires can replay after a change to an ignored or echo-only field. |
| Still being created | 429 with Retry-After of a second. The first request is between claim and insert. |

> Caution
>
> Once the order has finished, a replay can only return the original response if the result was stored. Data storage is off by default, so a replay after completion returns `410 result_unavailable`. Use a new key to execute again, or turn storage on in Settings.

A key lives for the longest of one day, your storage period, and the order's own lifetime plus an hour, so a replay always works for the full life of a queued order. The fingerprint covers the endpoint, the body, the `X-Price-Limit` and `X-Expires-After` headers, and the key's own price limit for the model.

Previous: [Batch API](https://openfill.ai/docs/batch). Next: [Data storage](https://openfill.ai/docs/storage).
