Open-model errors
Updated
- Should I retry?
- Status and code reference
- The 503 codes
- Retired plan limit recovery
- Envelopes that carry more than the table
- HTML 403 security checkpoint
- Payload too large
- Retry rules
- Related
Most /v1 routes return the chat-style error envelope below. /v1/messages and /v1/messages/count_tokens return the Messages error envelope instead.
| 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; read current capabilities, limits and prices from GET /v1/models.
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.
| 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 and 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 needs a pause before retrying.
Key creation and rotation errors (api_key_rotation_overlap_limit, api_key_mint_busy) are on Rotate, revoke, expire.
Status and code reference
| 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 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: 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 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. |
| 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. |
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 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.
| 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.
| 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_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 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.
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.
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. 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
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
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. 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.
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.
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 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-Keywhen you retry, streaming or not. - Respect
Retry-After, orRetry-After-Msif your client reads it, on every response that carries them. - On the hosted admission
429codes 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
409while the original is open, then the terminal usage and cost once it settles. See Idempotent retries.