Errors
Status codes, error bodies, and what to do about each.
Every error message says what to do next, so you can show it to whoever can act on it and branch your code on the HTTP status. Every response, errors included, carries an X-Request-ID; include it when you contact support.
Status codes
| Status | code | What to do |
|---|---|---|
| 400 | invalid_request, invalid_parameter, missing_required_parameter, unsupported_parameter, unsupported_content, unsupported_endpoint, context_length_exceeded | Fix or remove the parameter, content or endpoint the message names, then send again. |
| 401 | invalid_api_key, api_key_expired, api_key_disabled | Use a valid key from the API keys page. |
| 402 | insufficient_credits, api_key_spend_limit_exceeded | Top up on the Billing page, or raise the key's spending limit. |
| 403 | model_not_allowed, account_suspended | Add the model to the key's allowed models, or contact support. |
| 404 | model_not_found | Copy the model ID from GET /v1/models. |
| 413 | request_too_large | Send a smaller request body. |
| 429 | rate_limit_exceeded | Wait Retry-After seconds, then send again. |
| 500, 502, 503, 504 | model_error, model_overloaded, model_unavailable, model_timeout, service_unavailable, internal_error | Retry with backoff, or switch to another model. |
Parameter errors keep the model's own wording, so the message tells you the accepted range:
{
"error": {
"param": null,
"code": "invalid_request",
"message": "Invalid 'temperature': decimal above maximum value. Expected a value <= 2, but got 5 instead.",
"type": "invalid_request_error"
}
}Error body
The body follows the protocol of the endpoint you called, so your SDK raises its usual exception types.
{
"error": {
"param": "model",
"code": "model_not_found",
"message": "The model `no/such-model` does not exist. List available models with `GET /v1/models`.",
"type": "not_found_error"
}
}In the OpenAI shape, branch on code. The Anthropic shape carries the status category in error.type, and Gemini carries the code upper-cased in details[].reason.
With the OpenAI Python SDK:
import os
import openai
from openai import OpenAI
client = OpenAI(base_url="https://api.ohmygpt.com/v1", api_key=os.environ["OHMYGPT_API_KEY"])
try:
completion = client.chat.completions.create(
model="no/such-model",
messages=[{"role": "user", "content": "Hi"}],
)
except openai.RateLimitError as e: # 429
retry_after = int(e.response.headers.get("retry-after", "1"))
except openai.APIStatusError as e: # any other non-2xx
print(e.status_code, e.code, e.response.headers.get("x-request-id"))404 model_not_found 01a19b5f-6ab6-7dc0-88ff-f5c95e0169e4Errors during a stream
A stream that has started already has status 200. If generation stops partway, the stream ends with an error event in place of its normal final event:
data: {"error":{"param":null,"code":"model_error","message":"The model `zhipu/glm-5.3-flash` stopped mid-response, so the output is incomplete. Try again.","type":"server_error"}}The OpenAI and Anthropic SDKs raise this as an exception while you iterate, so handle it like any other error. If you parse the stream yourself, treat a stream that ends without its final event (data: [DONE], message_stop or response.completed) as incomplete.
When to retry
OhMyGPT already sends the request to another provider when one fails before answering, so you don't need retry logic for that. In your code:
- 400, 401, 402, 403, 404: change the request, key or balance first; sending it again unchanged gives the same answer.
- 429: wait
Retry-Afterseconds, then send again. - 500, 502, 503, 504: retry a few times with exponential backoff, waiting
Retry-Afterseconds when the header is present. If the error continues, switch to another model.