Errors
Errors come back with an HTTP status and a JSON body in the format of the endpoint you called, so your SDK raises its usual exceptions:
{ "error": { "message": "Your prepaid balance is used up. Add funds to continue sending requests." } }
The Messages endpoint uses Anthropic's shape, {"type": "error", "error": {"type": "...", "message": "..."}}. The message always says what went wrong and what to do about it.
Status codes
| Status | Cause | What to do | Retry? |
|---|---|---|---|
400 | The request is invalid: missing model, malformed JSON, an option the model doesn't support. | Fix the request. | No |
401 | The key is missing, malformed, deleted or rolled. | Check the key and header. | No |
402 | The balance or the plan credit is used up, or a plan's payment is outstanding. | Add funds or wait for the plan to renew; the message says which. | After fixing |
403 | The key's workspace can't use this model or endpoint, for example a plan key calling a model the plan doesn't cover. | Use a key from a workspace that can. | No |
404 | The model isn't offered on this endpoint (the message lists the models that are), the path doesn't exist, or the resource belongs to another project. | Check the model ID and endpoint. | No |
405 | The method isn't supported on this path. | Use the method in the Allow header. | No |
409 | An idempotency key was reused, or a resource isn't ready yet. | Use a new key for a new request; wait for the resource. | Sometimes |
413 | The request body is larger than 64 MiB. | Send a URL instead of inline data, or split the input. | No |
415 | The body isn't JSON or multipart form data. | Set Content-Type. | No |
429 | Too many requests, or too many in progress at once. | Wait for Retry-After. | Yes |
500, 502 | The model failed to complete the request. | Retry with backoff. | Yes |
503 | A temporary problem, including when your key or balance couldn't be checked. Nothing was sent or charged. | Retry shortly. | Yes |
Requests refused before a model ran — every 4xx above and the 503s — are not charged.
Retrying
Retry 429, 500, 502 and 503 with exponential backoff and jitter, and honour Retry-After when it's present:
async function withRetries(call, attempts = 5) {
for (let attempt = 0; ; attempt++) {
try {
return await call();
} catch (error) {
const status = error.status;
const retryable = status === 429 || status >= 500;
if (!retryable || attempt + 1 >= attempts) throw error;
const retryAfter = Number(error.headers?.["retry-after"]);
const delay = Number.isFinite(retryAfter) && retryAfter > 0
? retryAfter * 1000
: Math.min(30_000, 500 * 2 ** attempt) * (0.5 + Math.random());
await new Promise((resolve) => setTimeout(resolve, delay));
}
}
}
The OpenAI and Anthropic SDKs already retry these statuses a couple of times; raise maxRetries for batch jobs rather than wrapping them again.
Add an Idempotency-Key to requests you retry, so a retry after a network error can never be processed twice.
Errors in a stream
Once a stream has started, an error arrives as an event in the stream, not as an HTTP status. See streaming.
Model errors
When the model itself can't complete a request, the message is a neutral "The model could not complete this request. Please try again." Retry, or try a different model if it keeps happening.