Living Documentation

Troubleshooting by error family

Written by a person. Last read by a person on 2026-09-04, 4 days ago. Its facts were checked by the eval suite on 2026-09-07.

Something failed and you have a status code but no idea what it means, the same error is coming back after several retries, or you want to know which failures are worth reporting and which are yours to fix.

Organized by family, not by status code. The useful skill is sorting a failure into the right family. A reader who can do that can diagnose errors this page does not list. A reader who has learned twelve status codes by heart cannot.

Each family below names what else lives in it. That is the part worth reading even when your current problem is not in this family yet.

Before diagnosing anything: what this assumes

Stated up front so that a wrong assumption is visible rather than silently underneath everything.

This page assumes three things about your setup: that your process loaded the credentials you think it did, that it reached the network, and that the request arrived. If any one of those is false, every diagnosis below answers the wrong question, and it will answer it convincingly. The family that covers it is environment.

Non-API causes come first

Most messages that look like API problems are not. Before reading a request body, check in this order:

  1. Quota and billing. Has the organization hit a rate limit or a spend cap? These are organization-wide, so another process can exhaust them.
  2. Authentication. Is the key the one you think, in the environment you think, still valid?
  3. Network. Proxy, DNS, firewall, an idle-connection timeout on a long request.
  4. Environment. Is the code running against the configuration you edited?

Only then is the request itself worth reading. This ordering is not politeness; it is hit rate. A reader whose key expired is sent further from the answer by every paragraph about request shape.

The families

Authentication and permission

Statuses: 401 authentication_error, 403 permission_error. Retryable: no.

An expired, revoked or wrong-environment key looks exactly like a code problem from the inside. Check which key the process actually loaded before reading any request.

Also in this family: a key that works locally and fails in CI. A key without access to the model you named. An OAuth token sent in the wrong header.

Billing and spend limits

Statuses: 400, 402 billing_error, 429. Retryable: no.

The most confusing family, because it arrives wearing two others' clothes. A spend limit you set returns 400 invalid_request_error. Your tier's monthly cap returns 429 with no retry-after header. It keeps failing until the month rolls over. Identify it by error.details.error_code of enforced_spend_limit_reached.

Also in this family: an expired payment method, and a workspace limit lower than the organization's.

Rate limiting

Statuses: 429 rate_limit_error. Retryable: yes.

Check whether it is really your traffic. Limits are per organization, so another process, another environment, or a colleague's backfill spends the same budget.

Requests, input tokens and output tokens are limited separately. You can sit far below your request limit and still be throttled. Cached input tokens do not count toward the input-token limit on most models. That is why prompt caching is a throughput lever, not only a cost one.

Also in this family: acceleration limits, which can trip on a sharp ramp even below your steady-state ceiling.

Malformed request

Statuses: 400 invalid_request_error, 413 request_too_large. Retryable: no.

This is usually a serialization problem rather than a misunderstanding of the API, which is why re-reading the reference so rarely helps. The common causes are an object that stringified oddly, a field that arrived as null, and a document larger than it looked, given that requests cap at 32 MB.

Also in this family: a first message that is not from the user, and a parameter the model you named has removed.

Streaming state

Statuses: 200 (yes, really), 504 timeout_error. Retryable: yes.

The family with the worst failure mode, because its most common member returns success.

An error can arrive after a 200, partway through the stream. Error handling wrapped around the request has already returned by then and never fires. A truncated response looks complete to everything downstream.

At the client, a dropped connection looks just like a slow model. Check proxies and idle-connection timeouts on the path before you suspect the API.

Also in this family: a stream abandoned without being closed. It can also be a long non-streamed request killed by an idle timeout. That second one is what the 10-minute guidance exists to prevent.

Service capacity

Statuses: 500 api_error, 529 overloaded_error. Retryable: yes, with backoff.

Nothing about your request caused this and nothing about your request fixes it. The signal is that it fails identically regardless of what you send.

Also in this family: transient upstream errors, and overload during broad demand spikes.

Environment and network

Statuses: none. Retryable: yes.

The family that never reaches the API, and therefore never produces an API error to look up. It is last on this page and first in the diagnostic order, because it is the one people check last.

Also in this family: connection resets and TLS failures. It can also be a corporate proxy returning an HTML error page the SDK cannot parse as JSON.

How this fails

You will misfile things. The families overlap at the edges. A 400 from a spend limit you set is billing, wearing request-shape's status code. When a diagnosis is not working, the productive move is usually to re-sort rather than to dig.

A family can be right and useless. Knowing a failure is capacity tells you to back off. It tells you nothing about whether your backoff is correct.

Not yours to fix. Capacity errors, and permission errors on an organization you do not administer, are somebody else's. The signal is failing identically no matter what you change. Continuing to retry is how a five-minute conversation becomes an afternoon.

Always include the request ID

Every response carries a request-id header, and error bodies repeat it as request_id. In Python it is on the response object as _request_id. Log it. It is the difference between a support conversation that starts with the failure and one that starts with reconstructing it.