Errors
Every failure is JSON, with a stable machine-readable code:
{ "error": "quota_exceeded", "detail": "This request exceeds the quota on your plan." }
Branch on error. It is a stable string and will not change meaning. detail is a sentence for a
human, may be reworded at any time, and should be logged or displayed rather than matched.
Codes you should handle
These are the ones worth writing a branch for, because each implies something different to do.
| Status | error | Meaning | What to do |
|---|---|---|---|
| 401 | unauthorized | Missing, unknown, or revoked key. | Fix the credential |
| 402 | out_of_tokens | The account balance cannot cover this call. | Top up, then retry |
| 403 | forbidden | Valid key, but without the scope this endpoint needs. | Use a key that has it |
| 404 | not_found | No such resource, or not one this account can see. | Do not retry |
| 405 | method_not_allowed | Wrong HTTP method for this path. | Fix the request |
| 410 | gone | It existed and has expired. | Request it again |
| 429 | quota_exceeded · limit_reached | Past a quota or a cap on your plan. | Wait for the reset |
| 409 | not_ready | Still being produced. Its own status is pending, running, or ready. | Poll the status URL |
| 500 | internal_error | A fault on our side. It is logged. | Retry with backoff |
| 502 | unavailable · upstream_error · no_upstream · search_failed | A component of this request could not complete. Transient. | Retry shortly |
| 504 | timeout | Took too long to complete. | Retry |
Codes that mean the request was malformed
All 400, all fixed by changing the request, none worth retrying unchanged: bad_request,
invalid_request, invalid_json, query_required, question_required, messages_required,
id_required, invalid_path, invalid_repo, model_not_allowed.
The specific ones exist so you do not have to parse a sentence to find out which field was wrong.
Device authorisation
The device flow uses its own codes, matching the OAuth device grant: authorization_pending while
you should keep polling, slow_down if you are starting authorizations faster than the limit,
access_denied if the person declined, expired_token if the code aged out, and invalid_grant
for a code that was already exchanged. verification_failed means the challenge did not pass.
What errors deliberately do not tell you
A failure describes what happened to your request. It never names an internal component, and it never carries an upstream message, hostname, or trace id from anything behind the API.
The two distinctions that change what you do — is this mine to fix, and is retrying worth it — are in the tables above. Anything they do not explain is logged; quote the error code and the time, and we will read it.
Retrying
500, 502 and 504 deserve exponential backoff with jitter. 429 should wait for the reset
rather than retry quickly. Everything under 4xx will return the same answer until the request or
the key changes.
Requests are not automatically idempotent. A retried call that already had an effect can have it twice, so for anything that creates a resource, check whether it exists before retrying rather than assuming the first attempt did nothing.