Errors
The error envelope, every status code the API returns, and how to handle each.
Envelope#
Errors return a JSON body with a single error object:
json
{
"error": {
"message": "model is required",
"type": "invalid_request_error"
}
}Validation failures add an errors field with per-field detail:
json
{
"error": {
"message": "validation_failed",
"type": "invalid_request_error",
"errors": { "messages": "is required" }
}
}Status codes#
| Status | Type | Cause | Handling |
|---|---|---|---|
| 400 | invalid_request_error | Missing or malformed field, unsupported parameter, insufficient credits | Fix the request; don't retry unchanged |
| 401 | invalid_request_error | Missing, malformed, unknown, or revoked credential | Re-authenticate; for a Connect token, reconnect |
| 403 | invalid_request_error | Access denied — missing scope, or a model outside allowed_models | Request the scope, or change the model |
| 404 | not_found | Unknown path or model | Check the URL and model key |
| 413 | — | File upload over 10MB | Split or compress before uploading |
| 429 | rate_limit_error | Rate limited | Back off; honor Retry-After |
For persistent errors, contact support with the
response's log_id, if present, the model key, and the time of the request.
Common messages#
| Message | Meaning |
|---|---|
model is required | No model in the body |
messages must be a list | messages missing or not an array |
prompt is required | Image generation without a prompt |
Token is missing the models.run scope | Connect token can't run models |
Invalid or revoked connection token | The user disconnected your app |
Invalid API key | Unknown, revoked, or a suspended account |
Rate limit exceeded | Too many requests from this IP |
Retry policy#
| Situation | Retry? |
|---|---|
| 429 | Yes — after Retry-After, with exponential backoff |
| Timeout or 5xx | Yes — a small number of times, with backoff |
| 400 | No — the request is wrong |
| 401 | No — get a new credential first |
| 403 | No — the permission has to change |
js
async function withRetry(fn, attempts = 3) {
for (let i = 0; i < attempts; i++) {
const res = await fn();
if (res.status !== 429 && res.status < 500) return res;
const retryAfter = Number(res.headers.get('retry-after')) || 2 ** i;
await new Promise(r => setTimeout(r, retryAfter * 1000));
}
throw new Error('Exhausted retries');
}Insufficient credits#
When a user's balance can't cover a request, the request is rejected before the model runs — the balance is never left negative. Surface this to the user as "add credits", not as a generic failure. See Credits.