Errors
Status codes, error shapes per API format, and when to retry.
ORYNX returns errors in the format of the API you called, so your SDK raises its usual exception types. Every
response carries an x-request-id header (req_...). Include it when you report a problem.
Status codes
| Status | Meaning | Retry? |
|---|---|---|
400 | The request is invalid: bad JSON, no model, a model this route doesn't serve, or a provider rejection such as a prompt that is too long. | No, fix the request. |
401 | The key is missing, malformed, unknown, disabled, revoked or expired. | No. |
403 | The key may not use this model, or the calling IP is not on the key's allowlist. | No. |
404 | Unknown model or route, or a token counting endpoint. | No. |
413 | The request body is larger than 50 MB. | No. |
429 | A rate or concurrency limit, credit held by your own running requests, too many failed key attempts from your IP, or a busy model. | Yes, after Retry-After. |
500, 502, 503, 504 | ORYNX or the model provider failed, or the model took too long. | Yes, with backoff. |
A key that is out of credit gets 429 (400 on the Anthropic format) with no Retry-After. Don't retry it; the
request can't succeed until the key is topped up.
Out of credit
When a key has no credit left, each format gets the error its clients expect:
- OpenAI (Chat and Responses):
429with type and codeinsufficient_quota. - Anthropic:
400with typeinvalid_request_errorand a message that the credit balance is too low. The Anthropic API answers the same way, so Claude Code shows the error instead of retrying. - Gemini:
429with statusRESOURCE_EXHAUSTED.
If the credit runs out while a response is streaming, the stream ends with an error of the same type.
Before a request starts, ORYNX sets aside the most it can cost: its input plus the full output cap (max_tokens, or
the model's maximum output when you send none). Unused credit returns to the key as soon as the request finishes.
This means:
- When the key can't cover the full output cap, the request still runs, with a smaller cap that the key can pay for.
- Attachments count at their largest possible size (a PDF by its page count). Input the request doesn't carry
itself, such as
previous_response_idor GeminicachedContent, counts at the model's full context window. A key with a small balance may get the out-of-credit error for such a request. - If your own parallel requests have already set aside all the remaining credit, a new request gets a
429rate-limit error withRetry-After: 2. Retry it once they finish. Claude Code, Codex and the OpenAI and Anthropic SDKs retry it automatically.
Error shapes
{ "error": { "message": "Rate limit reached for this API key. Please slow down.", "type": "rate_limit_error", "param": null, "code": "rate_limit_exceeded" } }Errors after a response has started
Once a stream has started, the HTTP status is already 200, so a failure arrives as the last event:
- OpenAI Chat: a
data: {"error": {...}}line. - Anthropic: an
event: errorevent. - Responses: a
response.failedevent. - Gemini: an error object as the last
data:line, or as the last element of the JSON array.
The same applies to a non-streaming request that runs longer than 30 seconds: ORYNX has already sent status 200,
so check the JSON body for an error field. Treat a stream that ends without a finish reason as failed, and retry.
Provider messages
Errors that come from the model provider, such as "prompt is too long" or thinking-block validation errors, keep their original wording, so tools like Claude Code and Codex can react to them.