Error Codes

All errors from the API use a consistent JSON envelope matching the OpenAI error schema, and every error body carries the request id as error.request_id; API responses also send it in the X-Request-Id header. Quote it in any support email and we can pull the full trace.

A few responses come from our edge proxy instead and carry no request id: a 429 or 413 raised at the edge (same JSON shape), and a 502 or 504 HTML page. Retry a 502 with backoff. A 504 means a non-streamed request produced no output for 5 minutes; that request may still finish and be billed, so send long generations with stream: true instead of repeating the same call.

Error format

{
  "error": {
    "type": "invalid_request_error",
    "message": "Human-readable description of the error",
    "code": "machine_readable_code",
    "param": "field_name",
    "request_id": "req_86By4H0S9VTH8PneVWokg"
  }
}

Status code map

Statuserror.typeWhen you see itRetry?
400invalid_request_errorRequest body or parameters failed validation.No — fix the request.
401authentication_errorMissing, invalid, or revoked credentials.No — refresh credentials.
402insufficient_quotaCredit balance or a spend cap is exhausted, or (Student and Developer plans) today's free allowance is used up or not issued yet.No — top up or raise the cap. Exceptions: daily_allowance_pending clears by itself within a minute, so retry; daily_allowance_exhausted can clear by itself too when your organization turned off paying from its balance after the free allowance and requests still running hold the rest of it (its message says so).
403permission_errorAuthenticated, but not allowed (suspended org, model not enabled, IP blocked, etc.).No — fix the policy or pick another model.
404not_found_errorResource doesn't exist, isn't visible to you, or the endpoint isn't offered (route_not_found).No.
409conflict_errorIdempotency or uniqueness collision (e.g. coupon already used).No — resolve the conflict.
413invalid_request_errorRequest body over the size cap: 32 MiB on /v1/chat/completions, /v1/embeddings, /v1/decisions and /v1/systemone, 10 MiB on every other route. The message states the limit; base64 attachments count at their encoded size. See 413 — request too large.No — send fewer or shorter messages and tool outputs, or shrink attachments.
422invalid_request_errorJSON parsed but fields are invalid.No — fix the request.
429rate_limit_errorPer-key RPM ceiling hit (Retry-After 1–60 s; message quotes your effective limit), or an edge limit (per key, per IP, or platform-wide) (Retry-After: 1; message Too many requests at the edge; retry after 1 second.). On Student and Developer plans, also the plan's organization-wide RPM or too many requests at once, on the free-allowance models only (concurrency_limit_exceeded, allowance_capacity_busy). Also inflight_busy (Retry-After: 10), on /v1/chat/completions: your organization's own requests still running hold the credit this one needs, and it comes back as they finish (a streaming request first waits up to about 25 seconds for them, a non-streaming one up to about 10). Same JSON shape — honour the Retry-After header either way.Yes, after the window.
499(informational)Client closed the connection mid-stream. Billed for tokens produced up to the disconnect.n/a
500internal_errorUnhandled server-side fault.Yes, with backoff.
503service_unavailableThe model is temporarily unavailable — or, with code large_request_backpressure, a burst of very large requests briefly exceeded the platform's in-flight body budget (Retry-After: 2), or, with allowance_capacity_unavailable, a free-allowance model isn't available on our own GPUs right now, or, with model_overloaded (error.type service_unavailable_error), a model on our own GPUs is at capacity and your request could not start within the waiting limit (Retry-After 10–30 s), or, with spend_cap_unavailable, the monthly spend cap on your API key or project could not be checked right now, so the request was not run (Retry-After: 5).Yes, after Retry-After (30–60 s for a model outage).

400 — validation errors

CodeWhen
model_not_foundModel name not in your catalog. Check GET /v1/models.
model_not_supported_on_endpointModel exists but doesn't support this endpoint (e.g. embeddings model on /v1/chat/completions).
invalid_roleA message has a role other than system, developer, user, assistant, tool, or function.
missing_contentA message is missing required content.
max_tokens_exceededmax_tokens exceeds the model's context window.
empty_inputEmbeddings request with an empty input.
input_too_largeThe embeddings input array has more than 2,048 entries. Split it into smaller batches.
invalid_requestGeneric validation error. Check the param field.
provider_user_errorThe provider serving the model rejected the request; the message carries its reason (for example, prompt plus max_tokens over the context window). Fix the request; if the message doesn't point at your request, retry later or pick another model.
unsupported_parameterThe provider serving this model has no equivalent for a parameter you sent; param names it. Remove it or pick another model.
provider_prefix_mismatchYou pinned a provider with provider/model, but that provider does not serve the model.
engine_rejected_requestThe model engine rejected the request, usually because prompt plus max_tokens exceeds the context window.
model_does_not_support_imagesThe model does not accept images. Choose a vision-capable model or remove the image parts.
invalid_image_urlAn image or video URL is not https or a base64 data URI, is not publicly reachable, has an unsupported media type, or the inline image is over 5 MB.
remote_media_not_supportedThis model accepts images only inline, as base64 data URIs.
too_many_image_partsMore than 20 image or video parts in one request.
invalid_tool_choicetool_choice is not auto, none, required or a well-formed object.
invalid_logprobstop_logprobs was sent without logprobs: true.
missing_json_schemaresponse_format.type is json_schema but no schema was given.
auto_routing_disabledmodel: "auto" is not enabled. Choose a model explicitly.
prompt_too_long_for_planStudent and Developer plans: the prompt (on embeddings, the whole input batch, with param: "input") is longer than your plan's free allowance accepts on this model; the message quotes both sizes. Shorten the prompt or send fewer inputs per request, or use a pay-as-you-go model.
n_not_supported_on_planStudent and Developer plans: the free allowance runs one completion per request (n: 1) on its models. Send separate requests, or use a pay-as-you-go model.

401 — authentication errors

CodeMeaningFix
missing_auth_headerNo Authorization header, or a scheme other than Bearer (case-sensitive).Add Authorization: Bearer sk-... (or a dashboard session token).
invalid_api_keyAPI key not recognised, including keys from other providers that also start with sk-.Use a valid key from the dashboard.
invalid_tokenThe token doesn't start with sk- and isn't a valid, unexpired dashboard session token, e.g. an unfilled placeholder such as YOUR_API_KEY (message: Invalid or expired token).Send Authorization: Bearer sk-... with a key from the dashboard.
key_revokedThe key was revoked.Create a new key.
key_expiredThe key passed its expiry date.Create a new key; an existing key's expiry can't be changed.
invalid_token_typeWrong token type for this endpoint.Use the access token from login, or an API key for API calls.
token_revokedSession was ended (logout or revoke).Log in again.
user_not_found / user_disabledAccount no longer active.Contact your org owner or support.
not_a_memberYou don't belong to the selected organisation.Pick a different org in the dashboard.

402 — spending limits

402 means you need to act on billing before more traffic is accepted (unlike 429, which is temporary). Exceptions: daily_allowance_pending clears by itself within a minute, and daily_allowance_exhausted can clear by itself too when your organization turned off paying from its balance after the free allowance and requests still running hold the rest of it (its message says so). Branch on the error code and read the message, not on the status alone.

CodeWhere it firesResolution
daily_allowance_pendingStudent and Developer plans: today's free allowance for this model hasn't been issued yet. It is granted automatically within a minute of the Cairo day, or your plan, starting.Retry after a minute. Topping up also lets the model run now, charged to your balance at standard rates, unless your organization turned off paying from its balance after the free allowance (Billing → Plans).
daily_allowance_exhaustedStudent and Developer plans: today's free allowance for this model is used up, and your balance can't pay for the request, or your organization turned off paying from its balance after the free allowance (Billing → Plans).The allowance renews at 00:00 Cairo time. To keep going now, top up: the model is then charged to your balance at standard rates. If paying from the balance after the free allowance is turned off, topping up doesn't help: an owner can turn it back on under Billing → Plans. With it turned off, this code also answers while requests still running hold what is left of the allowance (retry in a moment) or when the request could cost more than what is left (shorten the prompt): read the message.
quota_exceededNo spendable credit left for a priced model: your balance, minus credit held by requests still running, is at or below 0 EGP — or below the small overdraft your organization gets once it has made a paid top-up (the amount is shown on your dashboard). When only your organization's own requests still running hold that credit, a chat completion naming its model gets 429 inflight_busy instead (retry it), and a model: "auto" request can get either: the 402 when what the running requests leave is too little for its classification step or for every model in its auto pool, the 429 when the chosen model's gate refuses it; embeddings, images and decision models keep this 402: its message counts the credit held by requests still running (with paying from the balance on, daily_allowance_exhausted says the allowance is used up instead), and a retry once they finish can succeed.Top up under Dashboard → Billing → Charge Balance or redeem a coupon. Models shown as Free in the Model Library need no credit.
insufficient_balanceYour spendable credit can't cover even the prompt plus a one-token reply (for images, the estimated image cost). A chat request you can only partly afford is not rejected: it is served with max_tokens lowered to what your credit covers (X-Output-Clamped header), so the answer may stop early with finish_reason: "length", streaming requests included. Exception: on a model that reasons out of sight (Claude, OpenAI GPT-5 and o-series, Gemini thinking models, Grok) a streaming request's max_tokens is never lowered below 1,024 output tokens, so you get this error when your credit covers fewer than 1,024 and your own max_tokens is larger. While your organization's other requests still running hold the credit, a chat completion naming its model gets 429 inflight_busy instead (retry it), after waiting for them up to about 10 s on a non-streaming request and up to about 25 s on a streaming one, and a model: "auto" request can get either: the 402 when what the running requests leave is too little for its classification step or for every model in its auto pool, the 429 when the chosen model's gate refuses it; the other routes keep this 402: its message counts the credit held by requests still running (with paying from the balance on, daily_allowance_exhausted says the allowance is used up instead), and a retry once they finish can succeed.Top up or shorten the prompt; on those reasoning models a max_tokens your credit covers is also served; for images, request fewer or smaller images.
spending_cap_reachedPer-key monthly spend cap hit; it resets at the start of the next month (Cairo time). The cap is a hard limit: you are never billed past it. A request the cap can only partly pay for is not rejected: it is served with max_tokens lowered to what the cap has left (X-Output-Clamped), streaming requests included; a non-streaming request without max_tokens is lowered to at most half of it, so another request can run beside it. When the rest of the cap is held by the key's requests still running, the request first waits for them (up to about 10 seconds, or about 25 on a streaming request) and, on a chat completion naming its model, answers 429 inflight_busy instead if they are still running: retry it (a model: "auto" request can get either, depending on whether its classification step or the chosen model's gate refuses it). There this 402 is for a cap with too little left even with nothing running; the other routes answer it while the key's running requests hold the rest, and the message says so.A key's cap can't be changed after creation: create a new key with a higher cap (or no cap) and switch to it, or wait for the reset.
project_spend_cap_exceededPer-project monthly cap hit. It is a hard limit too, with the same lowered max_tokens, and the same wait and 429 inflight_busy on a chat completion naming its model when requests still running hold the rest of it (model: "auto": either answer, as for the key cap).Raise the cap under Projects → Limits.

A stream that runs out of spendable credit part-way is not an error: it ends cleanly with finish_reason: "length", a usage block and stop_reason: "balance_exhausted" on its last chunk, then data: [DONE]; see Streaming. A stream that reaches its API key's or project's monthly spend cap part-way ends the same way, with stop_reason: "spend_cap_reached".

Example response:

{
  "error": {
    "type": "insufficient_quota",
    "message": "Monthly spend cap on this project has been reached. An owner can raise the cap under Project → Limits, or wait for next month's reset.",
    "code": "project_spend_cap_exceeded",
    "request_id": "req_86By4H0S9VTH8PneVWokg"
  }
}

403 — policy / permission denied

CodeMeaning
org_suspendedThe organisation has been suspended. Contact support.
email_not_verifiedDashboard/playground use before email verification is complete.
permission_deniedYour role doesn't allow this action.
ip_not_allowedRequest came from an IP not on the key's allow-list.
key_scope_insufficientThe key doesn't have permission for this endpoint.
model_not_availableModel isn't on your plan or org allow-list.
model_sunsetModel has been retired. See the changelog.
model_not_enabled_for_projectModel isn't enabled for this project.
project_not_in_orgSelected project doesn't belong to the active organisation.

404 / 405 / 422 — routing and schema errors

CodeMeaning
route_not_found (404)The endpoint isn't offered, e.g. /v1/responses or /v1/completions. Use /v1/chat/completions.
method_not_allowed (405)Wrong HTTP method for this path.
invalid_request_body (422)A field is missing, has the wrong type or is out of range; param names the first failing field.

413 — request too large

The raw JSON body of one request may be at most 32 MiB (33,554,432 bytes) on POST /v1/chat/completions, POST /v1/embeddings, POST /v1/decisions and POST /v1/systemone, and 10 MiB (10,485,760 bytes) on every other route. Base64 attachments count at their encoded size (4/3 of the file). Within a chat request, one message holds at most 2,000,000 characters of text and 5 MB (decoded) of media; over either limit you get 422 invalid_request_body. A request may carry at most 20 image or video parts; over that you get 400 too_many_image_parts.

CodeMeaningFix
request_too_largeThe body is over the route's cap. The message quotes the declared size (when the client sent Content-Length) and the limit.Not retryable. Drop earlier turns or tool outputs, or shrink attachments. Agentic clients should compact the conversation.
{
  "error": {
    "type": "invalid_request_error",
    "message": "Request body too large: 34,000,000 bytes declared; the limit for POST /v1/chat/completions is 33,554,432 bytes (32 MiB). Send fewer or shorter messages and tool outputs, or shrink base64 attachments.",
    "code": "request_too_large",
    "request_id": "req_86By4H0S9VTH8PneVWokg"
  }
}

429 — rate limits

CodeMeaningFix
rate_limit_exceededPer-key RPM ceiling exceeded; on Student and Developer plans, also the plan's organization-wide RPM on its free-allowance models, which all its keys share (the message says across your organisation; other models are not affected).Honour Retry-After. Past the plan's RPM you can also use a pay-as-you-go model. See Rate Limits.
concurrency_limit_exceededStudent and Developer plans: more requests running at once on the free-allowance models than your plan allows; the message quotes the limit.Retry after Retry-After (2 s), when one of your requests finishes.
allowance_capacity_busyThe free allowance's shared capacity is full right now.Retry after Retry-After (5 s), or use a pay-as-you-go model.
inflight_busyOn /v1/chat/completions: your organization's spendable credit is held by its own requests still running, and this one would fit once they finish. It counts across the whole organization, whichever key sent the request, and it also replaces a spend-cap 402 that only requests still running would cause. A streaming request (stream: true) first waits up to about 25 s for those requests and gets this only if they are still running; a non-streaming request gets it at once or after waiting up to about 10 s for them.Retry after Retry-After (10 s); the credit comes back as those requests end. Credit that is missing even with nothing running gets a 402 instead. So does a request to another route while running requests hold the credit: its message counts the credit held by requests still running (with paying from the balance on, daily_allowance_exhausted says the allowance is used up instead), and a retry once they finish can succeed. A model: "auto" request can get either: the 402 when what the running requests leave is too little for its classification step or for every model in its auto pool, the 429 when the chosen model's gate refuses it.

500 / 503 — server errors

CodeMeaningFix
internal_errorGeneric server fault.Retry with backoff; send X-Request-Id to support if persistent.
provider_backings_unavailableEvery provider serving the model failed for this request.Retry with backoff, or switch to another model from the model library.
no_callable_backingNo provider is currently enabled for this model.Retry later, or pick another model.
empty_completionThe model returned an empty response.Retry.
large_request_backpressureA burst of very large request bodies briefly exceeded the platform's in-flight body budget; your request was refused before it was read.Retry after Retry-After (2 s) — the official SDKs do this automatically.
no_healthy_backendsModel temporarily unavailable.Retry, or switch to another model from the model library.
engine_unavailableModel starting up or overloaded.Retry once.
model_overloadedThe model runs on our own GPUs and is at capacity: your request could not start within the waiting limit. Nothing was generated and you are not charged. Its error.type is service_unavailable_error.Retry after Retry-After (10–30 s) — the official SDKs do this automatically.
allowance_capacity_unavailableStudent and Developer plans: the model isn't available on our own GPUs right now, and the free allowance only runs there.Retry shortly, or use a pay-as-you-go model.
spend_cap_unavailableYour API key or project has a monthly spend cap, and the gateway could not check it right now, so the request was not run: a cap is never passed. Nothing was charged.Retry after Retry-After (5 s).

Student plan verification

These come from the Student card under Dashboard → Billing → Plans (the dashboard's /v1/auth/student/* endpoints), never from requests made with an API key. The card shows each message as it is.

CodeStatusMeaningWhat to do
student_verification_disabled503Student verification isn't available right now.Try again later.
email_not_verified403Your account email isn't verified yet.Verify it with the link we emailed you, then try again.
student_personal_org_required403The Student plan is for personal accounts, and this account has no personal organization.Contact support.
student_verification_revoked403Student verification is turned off for this account.Contact support if you think this is a mistake.
org_suspended403The account is suspended.Contact support.
student_not_eligible403This account isn't eligible for the Student plan.Contact support if you think this is a mistake.
student_phone_required403The account has no verified mobile number.Verify your number (the Student card offers it), then try again.
student_phone_not_owned403The mobile number on your account is verified on another account.Verify a number of your own.
invalid_student_email400The address isn't a valid email address.Enter your full university address, like name@university.edu.eg.
student_domain_not_supported400The address isn't from a supported university.Check the supported universities on the Student card.
student_already_verified409Your Student plan is active and can't be renewed yet; the message gives the date renewal opens (while the program's end is announced, only your plan's last day).Renew from that date.
student_account_email_first_time_only409Your account email can start the Student plan only the first time, and this account has verified before.On the Student card, verify your university email with a code.
student_plan_unavailable409The Student plan isn't open for new sign-ups right now.Try again later.
plan_signups_paused409New student sign-ups are paused. Students already on the plan can still renew.Try again later.
plan_program_ending409The Student plan has an announced last day, so it takes no new sign-ups or renewals.A running plan stays active until the last day in the message.
plan_program_ended409The Student plan has ended.—
student_email_already_used409This university address is already linked to another SovereignEG account. An address works for one account only, ever.Sign in to the account that verified it. Support can't move an address to another account.
expired_code400The code expired, or was already used.Request a new code.
invalid_code400The code is wrong.Check the latest email and try again.
too_many_attempts400Too many wrong codes.Request a new code.
rate_limit_exceeded429Codes were requested too often: from your account (per hour or per day), for that address, or from your network.Wait for the Retry-After time.
student_verification_budget_exhausted503Today's limit of verification emails is reached.Try again tomorrow.
student_verification_unavailable503Verification is temporarily unavailable.Try again in a few minutes.
student_email_delivery_failed503We couldn't send the code.Try again in a few minutes.
plan_term_conflict409Two verifications ran at the same time.Reload the Plans page: your plan may already be active.

Handling errors in Python

from openai import (
    APIConnectionError,
    APIStatusError,
    AuthenticationError,
    BadRequestError,
    PermissionDeniedError,
    RateLimitError,
)
 
try:
    response = client.chat.completions.create(
        model="gpt-oss-20b",  # live id from GET /v1/models
        messages=[{"role": "user", "content": "Hello"}],
    )
except AuthenticationError as e:
    print(f"Auth failed: {e.message}")
except PermissionDeniedError as e:
    print(f"Forbidden: {e.message}")
except BadRequestError as e:
    print(f"Bad request: {e.message}")
except RateLimitError as e:
    retry_after = e.response.headers.get("Retry-After", "60")
    print(f"Rate limited ({e.code}); retry after {retry_after}s")
except APIStatusError as e:
    if e.code == "daily_allowance_pending":
        print("Today's free allowance is being issued; retry in a minute")
    elif e.status_code == 402:
        print(f"Billing block: {e.message}")
    else:
        print(f"API error ({e.status_code}): {e.message}")
except APIConnectionError as e:
    print(f"Network error or timeout: {e}")

Handling errors in Node.js

try {
  const response = await client.chat.completions.create({/* ... */});
} catch (error) {
  if (error instanceof OpenAI.AuthenticationError) {
    console.error(`Auth: ${error.message}`);
  } else if (error instanceof OpenAI.PermissionDeniedError) {
    console.error(`Forbidden: ${error.message}`);
  } else if (error instanceof OpenAI.BadRequestError) {
    console.error(`Validation: ${error.message}`);
  } else if (error instanceof OpenAI.RateLimitError) {
    console.error(`Rate limited (${error.code})`);
  } else if (error.code === 'daily_allowance_pending') {
    console.error("Today's free allowance is being issued; retry in a minute");
  } else if (error.status === 402) {
    console.error(`Billing block: ${error.message}`);
  } else if (error instanceof OpenAI.InternalServerError) {
    console.error('Server error — retry');
  }
}

Getting help

Error Codes | SovereignEG Docs