# Open-model errors

[Open models](https://www.rightnowai.co/docs/open-models) / [Open-model errors](https://www.rightnowai.co/docs/open-models/errors)

Updated 2026-11-11

Most `/v1` routes return the chat-style error envelope below. `/v1/messages` and `/v1/messages/count_tokens` return the [Messages error envelope](https://www.rightnowai.co/docs/open-models/messages#errors) instead.

**Connection**

| Connection | Value |
| --- | --- |
| API base | `https://api.runinfra.ai/v1` |
| Workspace key | `rp_...` |
| Process variable | `RUNINFRA_GATEWAY_KEY` |
| Billing | The existing funding rules of your open-model account. |

Replace `MODEL_ID` with an available id from [Open models](https://runinfra.ai/inference-api?utm_source=rightnowai.co&utm_medium=referral&utm_content=docs-open-models); read current capabilities, limits and prices from `GET /v1/models`.

**JSON**

```json
{
  "error": {
    "message": "Invalid API key",
    "type": "authentication_error",
    "param": null,
    "code": "invalid_api_key"
  },
  "request_id": "req_example_0000"
}
```

`type` is the broad family, `code` is the specific reason, and `param` names the request field when one caused it. Branch on `type` and `code`, never on `message` text. Some errors add fields directly inside `error`, such as `paused_until`, `limit`, or credit amounts, never nested under a `details` property.

For a `401`, the wire code is `invalid_api_key`. Clients may still tolerate `auth_error` as a legacy spelling for that status.

On chat-compatible routes, `type` is one of exactly six values.

**Reference 29**

| error.type | Statuses that use it |
| --- | --- |
| `invalid_request_error` | 400, 405, 409, 413, 422 |
| `authentication_error` | 400, 401, 403 |
| `permission_error` | 402, 403 |
| `not_found_error` | 404 |
| `rate_limit_error` | 429 |
| `api_error` | 500, 502, 503, 504 |

Failures inside the gateway return the route's documented envelope, including a failure we did not anticipate. On chat-compatible routes, that arrives as `500` `internal_error` rather than an empty body. The platform edge can answer before the gateway with no JSON envelope: see [Payload too large](https://www.rightnowai.co/docs/open-models/errors#payload-too-large) and [HTML 403 security checkpoint](https://www.rightnowai.co/docs/open-models/errors#html-403-security-checkpoint).

Chat-compatible error responses carry `X-Request-Id`, matching `request_id`. Messages-compatible responses carry both `request-id` and `x-request-id`. Quote a request id when you contact support.

On a streaming response, HTTP `200` means the stream opened. It does not prove completion: an error frame or a connection closing without the protocol's terminal event is an incomplete response. Preserve the request id and reuse the same `Idempotency-Key` to read the original request's receipt without a second charge. Do not include authorization headers or request content in an error report.

## Should I retry?

The absence of the retry headers is a deliberate signal, not an oversight. A JSON `400`, `401`, `402`, `403`, `404` or `413` needs a different request or different credentials, so none carries timing. An [HTML 403](https://www.rightnowai.co/docs/open-models/errors#html-403-security-checkpoint) needs a pause before retrying.

Key creation and rotation errors (`api_key_rotation_overlap_limit`, `api_key_mint_busy`) are on [Rotate, revoke, expire](https://www.rightnowai.co/docs/open-models/authentication#rotate-revoke-expire).

## Status and code reference

**Reference 52**

| Status | error.type | error.code | What you should do |
| --- | --- | --- | --- |
| 400 | `invalid_request_error` | `invalid_json` | Send valid JSON. |
| 400 | `invalid_request_error` | `invalid_request` | Fix the field named by `param`. |
| 400 | `invalid_request_error` | `missing_required_field` | Add the field named by `param`. |
| 400 | `invalid_request_error` | `generation_count` | Reduce `n`, `best_of`, or another candidate-count field to four or fewer. |
| 400 | `invalid_request_error` | `context_length_exceeded` | The prompt is longer than the model's context window, and retrying it unchanged fails again. Compact the conversation or send a shorter prompt. `error.context_window` carries the window. On `/v1/messages` the same refusal reads `prompt is too long`; a streaming `/v1/responses` request receives it as a `response.failed` event. Nothing is charged. |
| 400 | `invalid_request_error` | `unsupported_model_operation` | Use an endpoint the selected model supports. |
| 400 | `invalid_request_error` | `hosted_capability_not_supported` | Drop the capability named by `param`, or call a model whose page lists it. |
| 400 | `invalid_request_error` | `hosted_parameter_not_supported` | Fix or remove the field named by `param`. For `response_format`, send the `reasoning_effort` named in the message (`none` on most affected models, `low` on another), or drop the format. The [model-specific source reference](https://runinfra.ai/docs/api-reference/chat-completions?utm_source=rightnowai.co&utm_medium=referral&utm_content=docs-open-models) identifies the cases. |
| 400 | `invalid_request_error` | `invalid_image_url` | The image named in the message failed the gateway's remote-image checks or memory bounds. Use a public PNG, JPEG, WebP or non-animated GIF within the [image limits](https://www.rightnowai.co/docs/open-models/chat-completions#three-things-a-request-can-be-refused-for): 20 MiB transferred and decoded per remote image, 50 MiB of expanded image data URLs and a 54 MiB body when remote images are present. An inline data URL also works. `param` is `messages`; no `Retry-After` is sent and `x-should-retry` is `false`. |
| 400 | `invalid_request_error` | `hosted_request_unprocessable` | `param` is `messages`. The model could not prepare this conversation within its time limit, or this exact request failed that way in the last 15 minutes and is not sent again yet. Compact or shorten the conversation, then retry. Very long runs of tool results in one turn are the usual cause. Retrying it unchanged does not help. |
| 400 | `invalid_request_error` | `upstream_error` | The model's server refused the request's content, for example a message or parameter shape it cannot serve. Retrying it unchanged fails the same way: fix the field named by `param` when present, otherwise simplify the request. Nothing is charged. |
| 400 | `invalid_request_error` | `invalid_idempotency_key` | Send a non-blank printable ASCII `Idempotency-Key` of 255 characters or fewer. |
| 400 | `invalid_request_error` | `idempotency_mismatch` | This key was already used with a different method, route, or body. Use a new key for a new request. |
| 400 | `invalid_request_error` | `invalid_trace_header` | Fix or remove the header named by `param`. `X-Client-Request-Id` must be non-blank printable ASCII of 512 characters or fewer. |
| 400 | `authentication_error` | `auth_error` | Use a workspace key with `https://api.runinfra.ai/v1`. |
| 401 | `authentication_error` | `invalid_api_key` | Send an active workspace API key in the Bearer header. |
| 402 | `permission_error` | `insufficient_credits` | Add enough credits for the reservation, then retry. |
| 402 | `permission_error` | `account_frozen` | Resolve the billing issue on the workspace, or add credits, then retry. Credits do not lift a hold for a payment dispute: contact support. |
| 402 | `permission_error` | `plan_limit_reached` | The plan is at a limit and neither credits nor Standby can serve the request. Follow `fixes` or wait for the stated reset or refill. |
| 402 | `permission_error` | `spend_limit_reached` | This key reached the monthly spending limit its owner set. Raise or remove the limit under Settings, API keys, or wait for `resets_at`. |
| 403 | `authentication_error` | `auth_error` | Replace a terminal, internal, or deactivated key with an active workspace API key. |
| 403 | `permission_error` | `credits_scope_violation` | The key is scoped to one deployment or endpoint, and `GET /v1/credits` describes the whole workspace. Use a workspace key. |
| 403 | `permission_error` | `usage_scope_violation` | The key is scoped to one deployment or endpoint, and `GET /v1/usage` describes the whole workspace. Use a workspace key. |
| 404 | `not_found_error` | `model_not_found` | Call `GET /v1/models` and use an id returned for the same key. |
| 404 | `not_found_error` | `unknown_endpoint` | The path is not a gateway endpoint, usually a `base_url` that already ends in the endpoint path or a doubled `/v1`. The message names the exact `base_url` to set: chat API clients use `https://api.runinfra.ai/v1`, Messages protocol clients use the bare host because the SDK appends `/v1/messages` itself. chat API-dialect responses also carry `error.base_url` with the value `https://api.runinfra.ai/v1`. |
| 405 | `invalid_request_error` | `method_not_allowed` | The route does not serve this HTTP method. Read the `Allow` header and send one of the methods it lists. |
| 409 | `invalid_request_error` | `idempotency_conflict` | Wait for `Retry-After`, then retry with the same key and body. |
| 413 | `invalid_request_error` | `payload_too_large` | Reduce the JSON request body below 32MB. It counts the whole request, including any conversation you resend, and an image is about a third larger once base64 encoded. |
| 422 | `invalid_request_error` | `idempotency_replay_unavailable` | Do not switch to a new idempotency key. Contact support with the request id. |
| 429 | `rate_limit_error` | `rate_limit_exceeded` | Wait for `Retry-After`. The per-key window and the [connection gate](https://www.rightnowai.co/docs/open-models/rate-limits#layer-1-the-connection-gate) both send this code. |
| 429 | `rate_limit_error` | `hosted_per_key_concurrency_limit` | Wait for `Retry-After`. It clears when a request using this key finishes. |
| 429 | `rate_limit_error` | `hosted_workspace_concurrency_limit` | Wait for `Retry-After`. It clears when a hosted request in the workspace finishes. |
| 429 | `rate_limit_error` | `hosted_shared_model_concurrency_limit` | Wait for `Retry-After`. Contact support if saturation continues. |
| 429 | `rate_limit_error` | `hosted_plan_concurrency_limit` | Wait for `Retry-After`. It clears when a plan-funded request in the workspace finishes. |
| 429 | `rate_limit_error` | `hosted_standby_workspace_limit` | Wait for `Retry-After`. It clears when a Standby request in the workspace finishes. |
| 429 | `rate_limit_error` | `hosted_standby_aggregate_limit` | Wait for `Retry-After`. It clears when a Standby slot on this model becomes available. |
| 429 | `rate_limit_error` | `hosted_standby_pressure` | Wait for `Retry-After`. It clears when load on the model drops. |
| 429 | `rate_limit_error` | `plan_window_busy` | Your running requests hold the rest of a coding plan limit. The server already waited up to 30 seconds. Retry after `Retry-After`; it clears when one of them finishes. |
| 429 | `rate_limit_error` | `hosted_workspace_tpm_limit` | Wait for `Retry-After`. When one request alone exceeds the limit, waiting cannot help, so reduce its token reservation. |
| 429 | `rate_limit_error` | `hosted_admission_congested` | Wait for `Retry-After`. The gateway shed the request under momentary load. It is not a quota. The wait is randomized between 1 and 4 seconds, up to 8 under heavy load, so refused clients do not return together. |
| 429 | `rate_limit_error` | `hosted_saturated_large_prompt` | The model is near its concurrency limit and very large prompts are deferred first. Wait for `Retry-After` and retry unchanged. |
| 429 | `rate_limit_error` | `hosted_model_at_capacity` | Every replica the request reached (at most two) is at capacity for the moment. Wait for `Retry-After` and retry unchanged; the same request succeeds once a slot frees. It is not a limit on your workspace, it carries no `limit` field and no `X-Hosted-Limit-Scope` header, and nothing is charged. See [the model at capacity](https://www.rightnowai.co/docs/open-models/rate-limits#layer-4-the-model-at-capacity). |
| 500 | `api_error` | `internal_error` | Retry after `Retry-After`. Include `X-Request-Id` in a ticket if it persists. |
| 500 | `api_error` | `credit_reservation_failed` | The credit reservation could not be recorded. Retry after `Retry-After`. |
| 502 | `api_error` | `response_format_error` | Retry the request. Contact support if it continues. |
| 502 | `api_error` | `upstream_error` | The model failed while serving the request, after it was admitted. Retry after `Retry-After` when present; otherwise back off briefly. |
| 502 | `api_error` | `upstream_http_error` | The model answered the request with an HTTP failure. Retry after `Retry-After` when present; otherwise back off briefly. |
| 504 | `api_error` | `hosted_inference_response_timeout` | The response exceeded its deadline, and no `Retry-After` is sent. For a non-streaming request, set `stream: true` or lower `max_tokens` before retrying. |
| 200 | `api_error` | `upstream_stream_error` | The stream ended early, on the same HTTP 200: its last frame carries this code, or `upstream_error` or `hosted_inference_response_timeout` when the gateway knows why, and no `[DONE]` follows. Treat a missing `[DONE]` as incomplete and retry. See [Streaming](https://www.rightnowai.co/docs/open-models/streaming#frame-shapes). |

On some models, when three prompts of about 400,000 tokens or more already run on a server, one more waits up to 30 seconds before `429 hosted_saturated_large_prompt`. The [model-specific error reference](https://runinfra.ai/docs/api-reference/errors?utm_source=rightnowai.co&utm_medium=referral&utm_content=docs-open-models) identifies the affected models. Use that refusal to identify the condition; wait for `Retry-After` and retry unchanged.

## The 503 codes

A `503` means the gateway could not safely serve the request. During a rate-limit store outage, each server applies rate limits on its own instead of returning `limiter_unavailable`, but hosted admission and `Idempotency-Key` checks can still return a retryable `503`. All of them carry `Retry-After` except `upstream_http_error`, which forwards one only when the model supplied it, and `hosted_inference_unavailable`, which carries none. For `datastore_unavailable`, `hosted_evidence_unavailable`, `hosted_admission_unavailable` and the connection gate's `limiter_unavailable`, the wait is randomized between 1 and 4 seconds, up to 8 under heavy load.

**Reference 108**

| error.code | What happened |
| --- | --- |
| `hosted_model_paused` | The model is temporarily paused. `error.paused_until` carries the next availability check. `Retry-After` is the time until that check, capped at one hour, and 60 seconds once the check time has passed. |
| `limiter_unavailable` | The rate-limit store is not configured, or the connection gate could not evaluate the request, so it was refused rather than served unmetered. |
| `hosted_admission_unavailable` | The capacity admission check could not complete, for example during a rate-limit store outage. |
| `hosted_evidence_unavailable` | The usage record that must exist before serving could not be written. No charge is incurred. |
| `hosted_inference_unavailable` | The model could not be served on this route. Retry shortly; contact support if it persists. |
| `datastore_unavailable` | The record the request depends on could not be read or written. Congestion alone is not this error and answers `429` `hosted_admission_congested` instead. |
| `idempotency_unavailable` | Your `Idempotency-Key` could not be checked, so the request was refused rather than risk running twice. Retry after `Retry-After` with the same key. |
| `billing_settlement_error` | Usage settlement did not complete. |
| `pricing_unavailable` | Pricing for the target could not be resolved. |
| `upstream_http_error` | The model answered the request with an HTTP failure. When the model's side supplied a `Retry-After`, it is forwarded unchanged; if the header is absent, back off briefly before retrying. |
| `upstream_error` | The open-models service could not reach the model for this request and did not complete it. |

## Retired plan limit recovery

Coding plans are retired for new customers. For existing holders of the open-models service, a serving coding plan returns `402 plan_limit_reached` when a plan window is at its limit and neither credits nor Standby can serve the request. The response carries `x-should-retry: false` and no `Retry-After`. It is a billing refusal; temporary Standby capacity refusals use `429` with retry timing.

The fields sit directly inside `error`, not under `details`. Read the blocking window, its exact counters, and the next step from your response.

**Retired plan error fields**

| Field inside error | Meaning |
| --- | --- |
| `limit` | `five_hour` or `week`. If both block, this identifies the blocking window that ends later, when both can admit again. |
| `resets_at` | ISO 8601 reset time for that window. |
| `tier` | `starter`, `pro`, or `team`. |
| `used_microcents`, `limit_microcents`, `pending_microcents` | The blocking window's settled usage, limit, and in-flight commitments. |
| `standby_resets_at` | ISO 8601 daily refill time. A refill does not remove a payment or coverage restriction. |
| `standby_blocker` | Why Standby cannot serve: see below. |
| `credits` | Credits-after-limits choice, cap in cents, balance in cents, spending and pending maximum holds in microcents, and `blocker`. |
| `fixes` | One next step with a machine-readable `code`, display `label`, and `url`, chosen for the blocker and caller's permission. |

**Standby refusal reasons**

| standby\_blocker | Meaning |
| --- | --- |
| `not_covered` | Standby covers chat requests only. |
| `payment_needed` | Standby is off until the plan payment goes through. |
| `credits_owed` | Standby is off while the credit balance is negative. |
| `closed` | Standby is unavailable. |
| `exhausted` | The daily amount, including in-flight commitments, has no room. |

`credits.blocker` is `disabled`, `balance_negative`, `key_limit`, `empty`, `cap`, or `period_unavailable`. An owner can receive **Keep full speed on credits**, **Add funds**, **Raise the key limit**, **Raise the cap**, or **Contact support** as the next step. A member receives **Ask an owner**.

The [funding headers](https://www.rightnowai.co/docs/open-models/usage#response-funding-headers) report `x-runinfra-funding: refused`. `/v1/messages` rewrites the body to `error.type: "billing_error"` and the message text, with `request_id`; the other plan fields are absent. In Messages clients such as Claude Code, read the message: it carries the limit, reset, Standby condition, and next step without an `error.code`. See [Messages](https://www.rightnowai.co/docs/open-models/messages#coding-plan-funding).

## Envelopes that carry more than the table

### 402 insufficient\_credits, with the balance and the top-up link

Credit fields are in cents. Read `current_balance_cents`, `required_cents` and `topup_url` from the response.

Add at least `required_cents - current_balance_cents`, then retry. `topup_url` is absolute.

When the workspace has a coding plan that is not paying for requests (a payment is overdue, or a payment dispute paused it), `message` ends with the plan's state and its next step, such as paying the overdue amount, or contacting support.

A `free_window_ended_at` field appears only when the model you called was on a promotional free window that closed within the last seven days, as a UTC RFC 3339 instant describing the same close the `message` states in words. When there is no recently closed window the field is absent rather than null, which is the usual case. The same field accompanies a `402` `account_frozen` under the same rule. A model's price, and whether it is inside a free window, live on that model's page, never here.

### 402 spend\_limit\_reached, with the limit, the spend, and the reset instant

A per-key monthly spending limit is optional and set by the key's owner under Settings, API keys. It is enforced on the key's settled spend in the current calendar month (UTC), so a request already in flight when the limit is reached may still complete. Amounts are in cents. These values are an example.

**JSON**

```json
{
  "error": {
    "limit_cents": 5000,
    "spent_cents": 5000,
    "resets_at": "2026-10-01T00:00:00.000Z",
    "message": "This API key has reached its monthly spending limit of $50.00 ($50.00 settled this month). Raise or remove the limit under Settings, API keys, or wait for the month to reset at 2026-10-01T00:00:00.000Z.",
    "type": "permission_error",
    "param": null,
    "code": "spend_limit_reached"
  },
  "request_id": "req_example_0011"
}
```

This describes the pay-as-you-go key limit. On a serving coding plan, the key limit blocks credits after plan limits; eligible plan or Standby usage can still run. See [key payment settings](https://www.rightnowai.co/docs/open-models/authentication#coding-plan-and-credits-only). Other keys in the workspace are unaffected. Raise or remove the limit on the key, use another key, or retry after `resets_at`.

### 503 hosted\_model\_paused, with the next availability check

`Retry-After` is in seconds: the time until the next availability check, capped at one hour. Once that check time has passed, it is 60 seconds on every endpoint, so a client polls about once a minute instead of every second. The status and code stay the same.

A paused model is not a missing model. It still appears in `GET /v1/models` with `availability: "paused"`, so treat this as retry and poll, never as a reason to drop the model id from your configuration.

### 422 idempotency\_replay\_unavailable, and why not to change the key

**JSON**

```json
{
  "error": {
    "original_status": 200,
    "original_response_bytes": 25000000,
    "message": "The original response for this Idempotency-Key was too large to replay safely. Do not retry with a new key because that can start new paid work. Contact support with the request ID.",
    "type": "invalid_request_error",
    "param": "Idempotency-Key",
    "code": "idempotency_replay_unavailable"
  },
  "request_id": "req_example_0012"
}
```

**Text**

```text
X-RunInfra-Idempotent-Replay: unavailable
```

A new key here would start new inference work and a second charge. Contact support with `request_id` instead.

### 400 hosted\_parameter\_not\_supported on response\_format

**JSON**

```json
{
  "error": {
    "message": "This model cannot enforce response_format while it is reasoning, so the reply would not match the format you asked for and the request would be billed anyway. Send \"reasoning_effort\": \"none\" together with response_format, or remove response_format. On /v1/responses the same remedy is a top-level \"reasoning_effort\": \"none\" beside \"text\": {\"format\": ...}.",
    "type": "invalid_request_error",
    "param": "response_format",
    "code": "hosted_parameter_not_supported"
  },
  "request_id": "req_example_0020"
}
```

Send the request again with the effort the message names beside `response_format`, or drop the format. Check the selected model's current JSON requirements in [A JSON format the model cannot hold while reasoning](https://www.rightnowai.co/docs/open-models/chat-completions#three-things-a-request-can-be-refused-for). Nothing is charged for the refusal, which is the point: the alternative is a `200` carrying a reply your parser rejects, billed in full.

### 429 hosted\_admission\_congested, a capacity shed

Under a momentary burst the gateway waits only briefly for capacity and then sheds the request rather than let it wait longer, so it never completes. That is a temporary capacity condition, not an outage and not a quota, so it answers `429`.

**JSON**

```json
{
  "error": {
    "message": "The gateway is at capacity and shed this request before completing it. This is a temporary condition on our side, not a problem with your request. Retry after the interval in the Retry-After header.",
    "type": "rate_limit_error",
    "param": null,
    "code": "hosted_admission_congested"
  },
  "request_id": "req_example_0018"
}
```

This answer applies on every `/v1` route, so the same condition never surfaces as a different status elsewhere. The message varies with where the shed happened; the status, `type` and `code` do not. Unlike a hosted admission limit, a shed carries no `limit` field and no `X-Hosted-Limit-Scope` header, because there is no quota to size against.

### 401 or 403 on a key that looks correct

Browser or device sign-in with `runinfra login` creates a terminal key. It has the same `rp_` prefix and 40 character body as a workspace API key and it cannot call the inference API, so it answers `authentication_error` with the message "This API key is internal and cannot be used for customer inference." Create a workspace API key in [Settings, API keys](https://runinfra.ai/settings/api-keys?utm_source=rightnowai.co&utm_medium=referral&utm_content=docs-open-models).

## HTML 403 security checkpoint

An HTML `403` security checkpoint instead of a JSON error means automatic traffic protection flagged your network. The response carries `x-vercel-mitigated: challenge`. Wait a few minutes before retrying, lower the request burst, and [contact support](mailto:jaber@runinfra.ai) if it persists.

## Payload too large

The 32MB ceiling is enforced on the declared `Content-Length` and again while the body streams, so an understated `Content-Length` does not bypass it. It is a limit on the encoded request and is separate from the model context window: split the prompt across requests or send less context.

A body above 32MB gets the same JSON `413` `payload_too_large`, with a request id, after the upload finishes. The one exception is a request above about 4MB that has no `Content-Length` header: the platform edge rejects it before the gateway sees it, with a `413` that carries no JSON envelope and no request id. Treat both as the same instruction: shrink the body, and send a `Content-Length` header (every SDK does).

## Retry rules

- Keep the same `Idempotency-Key` when you retry, streaming or not.
- Respect `Retry-After`, or `Retry-After-Ms` if your client reads it, on every response that carries them.
- On the hosted admission `429` codes the value is an estimate, capped at 8 seconds, and it changes between refusals. Add jitter if many workers share one key.
- Do not replace the key after `422 idempotency_replay_unavailable`.
- A streaming retry never replays the delivered tokens. The same key returns `409` while the original is open, then the terminal usage and cost once it settles. See [Idempotent retries](https://www.rightnowai.co/docs/open-models/idempotent-retries).

## Related

[### Rate limits What each 429 means, and how to back off.](https://www.rightnowai.co/docs/open-models/rate-limits) [### Idempotent retries Retry safely, streaming or not.](https://www.rightnowai.co/docs/open-models/idempotent-retries) [### Troubleshooting Symptom first, from the response you actually got.](https://www.rightnowai.co/docs/open-models/troubleshooting)

[Source reference](https://runinfra.ai/docs/api-reference/errors?utm_source=rightnowai.co&utm_medium=referral&utm_content=docs-open-models)

## Next

[Open-model limits](https://www.rightnowai.co/docs/open-models/rate-limits)
