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

# Errors

> Error format and every error code returned by the /v1 API.

Errors share one shape, compatible with OpenAI SDK error handling. Branch on `error.code`, not on the HTTP status or the message text.

```json theme={null}
{
  "error": {
    "code": "model_unavailable",
    "message": "The model does not exist or is disabled.",
    "type": "not_found_error"
  },
  "request_id": "2f6c1d0e9a"
}
```

| Field | Meaning |
| - | - |
| `error.code` | Stable machine-readable code from the tables below. This is the contract to branch on. |
| `error.message` | Short English description for logs and SDK exceptions. It can change wording at any time and never contains the provider's own error text. |
| `error.type` | Error family by HTTP status: `invalid_request_error` (400, 413), `authentication_error` (401), `insufficient_quota` (402), `permission_error` (403), `not_found_error` (404), `conflict_error` (409), `rate_limit_error` (429), `service_unavailable` (503) or `api_error` (other 5xx). |
| `error.param` | Optional. The rejected request field for parameter errors. |

`request_id` is present on chat completion, Responses and embeddings errors and is also sent as the `X-Request-ID` header. Keep it: support uses it to look up a single call. The console's **Usage** page always shows totals and charts; the per-request list is off by default for customer accounts, so quote the request ID to [support@omnirelay.ai](mailto:support@omnirelay.ai) when you need one call checked.

For request parameter errors, optional `error.param` identifies the rejected field, for example `temperature` or `reasoning.effort`. It contains no parameter value. Check both the field capability and its combination with the selected reasoning effort. Responses reports native field names. Local budget/protocol validation happens before dispatch. When the provider rejects the request with HTTP 400 or 422, the hold is released and nothing is charged; a recognized code and safe field path are returned, otherwise `invalid_parameter`. Some models ignore unsupported fields instead of rejecting them; such calls succeed without `error.param`.

```json theme={null}
{"error":{"code":"unsupported_parameter","message":"This model does not support one of the request parameters. Nothing was charged.","type":"invalid_request_error","param":"temperature"},"request_id":"your-request-id"}
```

A stream that fails after HTTP 200 sends the same `error` object in a final `data:` event (Chat Completions) or an `error` event with `code` and `message` (Responses), and does not end with `[DONE]`.

## Error codes

| Status | Code | Meaning | What to do |
| - | - | - | - |
| 400 | `invalid_json` | The body is not valid JSON. | Fix the payload. |
| 400 | `invalid_input` | A field is missing, empty, too long or of the wrong type. | Check `model` and `messages`. |
| 400 | `unsupported_parameter` | The selected model does not support this field or capability. Nothing is charged. | Check the model contract and remove the field. |
| 400 | `invalid_parameter` | A live field value, output limit or message is invalid. Nothing is charged. | Check limits and parameter values. |
| 400 | `context_length_exceeded` | The input plus the output limit does not fit the model's context window. Nothing is charged. | Shorten input or lower the output limit. |
| 400 | `stream_usage_not_supported` | The model cannot stream with usage reporting. Nothing is charged. | Send the request without `stream`. |
| 400 | `unsupported_protocol` | The model is not available on this endpoint. Nothing is charged. | Use the endpoint the model supports. |
| 403 | `ip_not_allowed` | The API key has an [IP allowlist](https://omnirelay.ai/docs/overview/rate-limits.md#key-ip-allowlist) that does not include the calling address. Nothing is charged. | Call from a listed address, or change the key's allowlist in the console. |
| 403 | `model_not_allowed` | The API key is [limited to other models](https://omnirelay.ai/docs/overview/authentication.md#limit-a-key-to-some-models). Nothing is charged. | Call a model the key allows, or change the key's limit in the console. |
| 401 | `management_key_required` | An API key was sent to the [Management API](https://omnirelay.ai/docs/general/management-api.md). | Use a management key. |
| 403 | `activation_required` | Key creation requires verified workspace activation. | Check Overview and complete activation before creating a key. |
| 403 | `insufficient_scope` | A `read` management key attempted a write. | Use a `write` management key. |
| 400 | `invalid_tags` | A tag breaks the [tag rules](https://omnirelay.ai/docs/general/tags.md#rules): more than 10, a repeated key, a disallowed character, or a key or value that is too long. | Fix the `X-OmniRelay-Tags` header, the `tags` field or the key's default tags. |
| 401 | `unauthorized` | The key is missing, unknown or revoked, or the account is suspended. | Create a new key. |
| 401 | `request_no_longer_authorized` | The key or access changed while the call was starting. Nothing was sent or charged. | Check the key and retry. |
| 402 | `insufficient_balance` | The balance does not cover this request. | Top up in the console. |
| 402 | `inference_budget_or_access_denied` | The live request failed the atomic funding or access check before dispatch. Nothing is charged. | Check available wallet balance, outstanding holds, key budget and model access. The balance and budget must cover the maximum reservation, even for a short prompt. If these checks pass, contact support to confirm workspace capacity. |
| 402 | `key_budget_exceeded` | The key reached its budget. Nothing is charged. | Raise the budget or use another key. |
| 402 | `member_budget_exceeded` | Your [member budget](https://omnirelay.ai/docs/general/usage-billing.md#member-budgets) for this period is used up. It covers every key you hold and the playground. Nothing is charged. | Ask your administrator to raise it, or wait for the next period. |
| 404 | `model_unavailable` | The model does not exist or is disabled. | Call `GET /v1/models`. |
| 404 | `not_found` | Unknown `/v1` route. | Check the path and method. |
| 409 | `request_conflict` | A call with this request ID is already being processed. | Do not resend; wait for the first call. |
| 413 | `payload_too_large` | The request exceeds the selected model’s published size limits. | Shorten the conversation. |
| 429 | `rate_limited` | The workspace, member or key used its [calls for this minute](https://omnirelay.ai/docs/overview/rate-limits.md). Nothing is charged. | Retry after the `Retry-After` seconds. |
| 429 | `concurrency_limited` | The workspace, member or key already has its maximum [calls in progress](https://omnirelay.ai/docs/overview/rate-limits.md). Nothing is charged. | Retry when a call finishes (`Retry-After: 1`). |
| 429 | `upstream_rate_limited` | The model provider refused the call for your account's rate limit before processing it. The reservation was released; nothing is charged. | Retry shortly (`Retry-After: 1`). |
| 503 | `channel_unavailable` | The model is temporarily unavailable. Nothing is charged. | Retry later or pick another model. |
| 503 | `live_provider_not_configured` | The model is a live model and live inference is not enabled yet. | Check live activation and contact support. |
| 503 | — | Workspace opening, rotation or credential verification is incomplete. The call is not sent to the model provider. | Wait for activation or contact support with the request ID. |
| 503 | `runtime_maintenance` | New live calls are temporarily paused for maintenance. The call is not sent to the model provider. | Wait a few minutes and retry. |
| 503 | `storage_unavailable` | Temporary storage failure; dispatch may already have happened. | Contact support with the request ID before resending. |

## Provider errors (`upstream_*`)

These codes mean the call reached the model provider and something went wrong there. Whether anything was charged depends on whether model output had already reached you:

* **Nothing was delivered:** the reservation is released at once and nothing is charged. This is always the case for a non-streamed call, and for a stream that fails before its first output.
* **Output was already delivered** (a stream that breaks midway), or **the provider's answer arrived but could not be verified:** the call is held for review and settled from the provider's usage record. Do not resend it automatically; contact support with the request ID if the hold does not clear.

| Status | Code | Meaning | Charged? |
| - | - | - | - |
| 502 | `upstream_rejected` | The provider refused the call (a non-2xx reply other than 400, 422 or 429). | No. |
| 502 | `upstream_transport_error` | The connection to the provider failed. | No, unless output had already been delivered. |
| 502 | `upstream_stream_incomplete` | The provider's stream ended before the answer was complete. | No, unless output had already been delivered. |
| 502 | `upstream_stream_error` | The provider's stream reported an error. | No, unless output had already been delivered. |
| 504 | `upstream_timeout` | The provider did not respond in time. | No, unless output had already been delivered. |
| 499 | `upstream_cancelled` | The client cancelled the call before it finished. | No, unless output had already been delivered. |
| 502 | `upstream_invalid_response` | The provider's answer arrived but failed validation (also reported as `upstream_invalid_json`, `upstream_invalid_content_type`, `upstream_model_mismatch`, `upstream_unsupported_delta`, `upstream_premature_usage`, `upstream_duplicate_usage`, `upstream_response_too_large` or `upstream_body_too_large`). | Held for review. |
| 502 | `upstream_invalid_tool_call` | The provider returned a tool call that does not match the declared tools. | Held for review. |
| 502 | `upstream_invalid_structured_output` | The provider's structured output does not match the requested schema. | Held for review. |
| 502 | `usage_missing`, `usage_invalid`, `usage_unsupported` | The answer arrived without valid usage to bill it. | Held for review. |
| 502 | `cost_exceeds_hold` | The answer cost more than its reservation. | Held for review; not billed automatically. |
| 503 | `settlement_conflict` | The call finished but could not be recorded. | Held for review. |
| 502, 504 | `image_call_held` | An [image call](https://omnirelay.ai/docs/images/image-generation.md#failures-and-charges) timed out, lost its connection, met a provider server error or returned an answer that could not be verified. | Held for review; charged only if the images were produced. |
| 502, 504 | `video_submit_held` | A [video task](https://omnirelay.ai/docs/videos/video-generation.md#failures-and-charges) could not be confirmed when it was submitted: a timeout, a lost connection or a provider server error. | Held for review; charged only if the video was generated. |

An image call that runs longer than about 25 seconds answers `200` before it finishes, so any of these errors can also arrive as the JSON body of a `200` response. Check the parsed body for `error` first.

`upstream_rate_limited` (429) is listed above: it is a refusal before processing and is never charged.

## Retry guidance

For live inference, an HTTP status alone does not prove that nothing was billed, so disable automatic SDK retries and decide from `error.code`:

* Codes documented as **not charged** (all 4xx refusals, `upstream_rejected`, `channel_unavailable`, `runtime_maintenance`) can be retried. Use backoff for `rate_limited`, `concurrency_limited` and `upstream_rate_limited`.
* `upstream_transport_error`, `upstream_timeout` and the stream codes are not charged for a non-streamed call or a stream that delivered nothing; retry those.
* For codes **held for review**, or a stream that broke after delivering output, do not resend automatically. Check your balance and contact support with the request ID if you need the outcome confirmed.

```python Python theme={null}
from openai import APIStatusError

try:
    client.chat.completions.create(model="gpt-6-luna", messages=messages)
except APIStatusError as error:
    code = error.response.json()["error"]["code"]
    if code == "insufficient_balance":
        ...
```


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