Reference
Errors
Every error uses one envelope: OpenAI’s message, type and code, plus a retryable hint and the request ID. Provider secrets and stack traces are never returned.
Error envelope
402 Payment Required
{
"error": {
"message": "Balance $0.012000 does not cover this request's maximum cost of $0.320040. Top up, or lower max_tokens.",
"type": "insufficient_quota",
"code": "insufficient_balance",
"retryable": false,
"request_id": "req_QXt5TzjNYPURUd0"
}
}Branch on code, not on message; messages may be reworded. Because the shape matches OpenAI’s, existing SDKs and tools surface the message as-is. Log request_id; failed requests have a receipt too.
Error codes
| Code | HTTP | Retryable | What to do |
|---|---|---|---|
invalid_api_key | 401 | No | Check the key is whole, starts with iq_, and is sent as a Bearer token. |
key_revoked | 401 | No | Use an active key. Revocation can’t be undone. |
key_expired | 401 | No | The key passed its expiry date. Create a new one. |
invalid_request | 400 | No | The body isn’t valid JSON or is missing model or messages. The message names the field. |
unsupported_model | 400 | No | Use an alias or a model id from GET /v1/models. |
unsupported_parameter | 400 | No | No candidate model can serve this request. The message says why for each one. |
model_not_allowed | 403 | No | Use a model this key allows, or create a key for it. For a fix, the key must allow the tier’s alias. |
insufficient_scope | 403 | No | The key lacks the scope: inference for chat, fix for fix agents. Create a key with it. |
insufficient_balance | 402 | No | Add balance, or lower max_tokens (or pick a cheaper fix tier) so the worst case fits. |
budget_exceeded | 402 | No | This key reached its daily limit, or the account its monthly budget. Wait for the reset, or raise the limit. |
cost_cap_reached | 402 | No | A fix would need more than its approved maximum. Get a new quote, or pick a stronger tier. |
rate_limit_exceeded | 429 | Yes | Too many requests on this key. Wait for the retry-after header. |
not_found | 404 | No | Wrong path, or a receipt or fix run that isn’t on your account. |
quote_expired | 410 | No | Fix quotes last five minutes. Get a new quote. |
context_too_large | 413 | No | Too much input for the fix agent or tier. Send the failing part of the log and the files it points at. |
provider_rate_limited | 429 | Yes | Every candidate was rate-limited. Retry with backoff, or use an alias with more models. |
provider_timeout | 504 | Yes | No candidate answered in time. Retry. |
provider_unavailable | 503 | Yes | Candidates were down or overloaded, or no model is connected for this fix tier yet. Retry. |
provider_bad_request | 400 | No | The provider rejected the request; its message is included. Fix the request. |
provider_invalid_response | 502 | Yes | The provider sent something malformed. Retry. |
provider_auth_error | 502 | No | Inferiq’s own provider credentials failed. Not your key. Report the request_id. |
request_cancelled | 499 | No | You closed the connection. Recorded on the receipt as cancelled. |
internal_error | 500 | Yes | Retry with backoff. Report the request_id if it persists. |
Provider errors are only returned after every candidate behind your alias has been tried.
Retrying safely
Retry only when retryable is true, with exponential backoff and jitter. The gateway has already tried the other models behind your alias, so keep client-side attempts low. A request refused before a provider was called costs nothing, so retrying it later is free.
retry.ts
import OpenAI from "openai";
const client = new OpenAI({ baseURL: process.env.INFERIQ_BASE_URL, apiKey: process.env.INFERIQ_API_KEY });
async function ask(body: OpenAI.ChatCompletionCreateParamsNonStreaming, attempts = 3) {
for (let i = 0; ; i++) {
try {
return await client.chat.completions.create(body, { maxRetries: 0 });
} catch (err) {
const info = err instanceof OpenAI.APIError ? (err.error as { retryable?: boolean } | undefined) : undefined;
if (!info?.retryable || i === attempts - 1) throw err;
await new Promise((r) => setTimeout(r, 500 * 2 ** i + Math.random() * 250));
}
}
}