Skip to content

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

CodeHTTPRetryableWhat to do
invalid_api_key401NoCheck the key is whole, starts with iq_, and is sent as a Bearer token.
key_revoked401NoUse an active key. Revocation can’t be undone.
key_expired401NoThe key passed its expiry date. Create a new one.
invalid_request400NoThe body isn’t valid JSON or is missing model or messages. The message names the field.
unsupported_model400NoUse an alias or a model id from GET /v1/models.
unsupported_parameter400NoNo candidate model can serve this request. The message says why for each one.
model_not_allowed403NoUse a model this key allows, or create a key for it. For a fix, the key must allow the tier’s alias.
insufficient_scope403NoThe key lacks the scope: inference for chat, fix for fix agents. Create a key with it.
insufficient_balance402NoAdd balance, or lower max_tokens (or pick a cheaper fix tier) so the worst case fits.
budget_exceeded402NoThis key reached its daily limit, or the account its monthly budget. Wait for the reset, or raise the limit.
cost_cap_reached402NoA fix would need more than its approved maximum. Get a new quote, or pick a stronger tier.
rate_limit_exceeded429YesToo many requests on this key. Wait for the retry-after header.
not_found404NoWrong path, or a receipt or fix run that isn’t on your account.
quote_expired410NoFix quotes last five minutes. Get a new quote.
context_too_large413NoToo much input for the fix agent or tier. Send the failing part of the log and the files it points at.
provider_rate_limited429YesEvery candidate was rate-limited. Retry with backoff, or use an alias with more models.
provider_timeout504YesNo candidate answered in time. Retry.
provider_unavailable503YesCandidates were down or overloaded, or no model is connected for this fix tier yet. Retry.
provider_bad_request400NoThe provider rejected the request; its message is included. Fix the request.
provider_invalid_response502YesThe provider sent something malformed. Retry.
provider_auth_error502NoInferiq’s own provider credentials failed. Not your key. Report the request_id.
request_cancelled499NoYou closed the connection. Recorded on the receipt as cancelled.
internal_error500YesRetry 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));
    }
  }
}