The first minute of an LLM API failure is usually wasted on the wrong question. "Which documentation page covers this" is the wrong question; the right one is "which class of failure am I looking at", because the classes need opposite responses. A rate limit wants patience, a permission error wants configuration, a retired model wants a migration, and a provider-side fault wants a status page and a retry budget. This page is the entry point to that sorting: one table keyed by symptom, one ordered set of criteria for assigning a class, and links to the detailed guides rather than a second copy of them. The evidence for the sorting is in what the failed request returned, so read the response before the documentation.
Everything below is method, not vendor trivia. The official error taxonomies are linked at the end for the class definitions themselves, and the detailed treatments live in the articles this page points to. Verify behavior against the official pages current when you debug.
The symptom table
Find the row that matches what you actually see in the response or the client, not what you assume caused it.
| Symptom | First thing to check | Common misread | Detail |
| --- | --- | --- | --- |
| 401 family | Whether the credential presented is valid, unexpired, and the one you think it is | Treating it as a quota problem and retrying | error code guide |
| 403 | Whether the key's organization, project, or workspace actually has permission, and whether a region policy applies | Retrying as if it were a rate limit | Permission section below |
| 404 | The model or resource identifier: whether it exists, whether it is spelled as published, and whether your account can see it | Renaming the model to make the error disappear | Model-identity checks below |
| 429, rate limit | The retry-after header, when present, and whether this is a rate limit at all | Retrying a spend-cap stop forever | Boundary one below |
| 5xx | Whether the error is provider-side, and whether the same request fails without your changes | Blaming your payload for a provider fault | Anti-patterns below |
| Timeout or connection break | Whether the failure is at connect time, during response, or after streaming began | Adjusting application logic when the network is the problem | Streaming section below |
| Stream ends early | The last event received, how long the stream was idle, and whether an error event arrived after the 200 | Assuming the model finished | Streaming section below |
| 200 but wrong content | Whether the request shape, the model identifier, and the output parsing still match what you intended | Treating a shape mismatch as a model quality problem | Routing section below |
The table is deliberately shallow. Its job is to send you to the right second step in under a minute.
Four routing criteria, in order
Do not skip ahead. The order matters because each step narrows the next one, and because the cheapest checks come first.
First, the error code and response headers. The status family tells you which bucket you are in: authentication, permission, not-found, rate, server. Then read the headers and fields that identify the request and its retry guidance. Anthropic's error documentation notes that every response carries a request-id header and that error bodies include the same identifier as a request_id field, which is the value support will ask for. The presence or absence of retry-after is itself a signal, and the next section shows why.
Second, the account and permission context. Which organization, project, or workspace is the request actually running under? Which scope does the key carry? Does a region or policy restriction apply? Most 403s and a surprising share of 401s are answered here, and no amount of retrying changes them.
Third, the model identity. Is the model string one that exists on the official page today? Is it an alias that routes somewhere you did not expect, or a snapshot that has been retired? A 404 or an unexpected output can both trace back to this step.
Fourth, provider status and quota state. Only now ask whether the provider is healthy and whether your allowance is intact. This is where the rate-limit versus spend-cap distinction lives, and it is the boundary that costs teams the most time.
Boundary one: a rate limit is not a spend-cap stop
Both can arrive in the 429 family, and both can carry rate_limit_error as the type, which is exactly why they get confused. Keep the rate limit vs exhausted quota distinction straight, because the handling is opposite.
A rate limit is temporary: the platform is telling you to slow down, and when a retry-after header is present, waiting that long is the documented first move. Backoff with jitter and a total retry budget are the right client behavior, and the discipline is covered in the retry budget guide.
A spend-cap stop is not temporary in the same sense. Anthropic's error documentation describes the 429 family as covering a rate limit, a usage-tier monthly spend cap, or a Claude Code workspace spend limit, and states that a tier spend-cap 429 has no retry-after header and keeps failing until access resumes. On the Messages API the distinguishing field is error.details.error_code with the value enforced_spend_limit_reached, as covered in the multi-tenant cost and quota guide. Retrying, including an SDK's automatic retries, does not restore access; only the budget situation changing does.
The one-glance test, then: if a 429 has a retry-after, treat it as pacing. If it has no retry-after and the body says the spend cap was reached, stop the retry loop, alert, and fix the budget or the plan. The same family split exists elsewhere: OpenAI's error table lists credit-balance exhaustion and rate limiting under 429 with different causes and different remedies. The class is decided by the cause, not by the status code, and an exhausted quota is not a pacing problem. Treating the second as the first is how an incident turns into a retry storm.
Boundary two: 403 is not 404
These two look like neighbors and belong to different departments.
A 403 says the request was understood and refused: the credential lacks permission for this resource, or a region or policy restriction applies. The fix is configuration or authorization, and the 403 guide covers how to tell the permission case from the region case.
A 404 says something named in the request does not exist or is not visible to this account: a path, a resource ID, or a model identifier. Two self-checks resolve most of them. First, compare the model string character by character with the official model page, including any alias the SDK inserts. Second, confirm the account, project, or workspace actually has access to that model; a model that exists but is not enabled for your organization behaves like a missing one.
Renaming the model until the error disappears is not a diagnosis. It hides a permissions problem behind a different model, and the same failure returns the next time someone changes the code.
When a stream ends early
A broken stream has two possible homes: your reader or their writer. Separate them with three observations.
First, the last event type you received. If the stream ended without the terminal event the API documents, it was cut, not completed. Second, the idle gap before the break: a long silence followed by a disconnect looks like an idle timeout, while an immediate close looks like a refused or misrouted request. Third, whether a 4xx or 5xx accompanied the break. Anthropic's error documentation notes an important wrinkle: with server-sent events, an error can occur after the API has already returned a 200 response, in which case the standard error mechanisms do not apply and the error arrives as a stream event. A client that only checks HTTP status will read that as "success with a short answer".
The full treatment, including event ordering and idle handling, is in the streaming guide.
What every failure should leave behind
Whatever the class, the record should answer the same five questions: which request (request-id), which model actually responded (not which one you configured), which error type and code, whether retry-after was present, and how long it took with how many retries. The field-level discipline, including what never belongs in logs, is in the logging guide.
Two exclusions are absolute: credentials never go into failure logs, and neither does raw user content. A debugging record that contains either has created a second incident while investigating the first.
Anti-patterns that keep recurring
- Treating a 403 as a rate limit and retrying with backoff, which burns the retry budget on a permission problem.
- Retrying 5xx without a ceiling, turning a provider outage into a self-inflicted traffic spike.
- Changing the model name to silence a 404 instead of checking access.
- Ignoring
retry-afterand inventing a backoff schedule that is shorter. - Reading only client logs while never looking at response headers and error bodies.
- Logging full error payloads in production without a redaction policy.
- Assuming a 200 means the stream completed.
The checklist
- Read the status family and the response headers before anything else.
- Assign one of the four classes using the ordered criteria.
- Decide the action: retry with a budget, fix configuration, fix the model identity, or report upstream.
- Record
request-id, the model that actually responded, the error type and code, andretry-afterpresence. - Confirm the failing request shape against the endpoint's documented contract.
- Add the case to the team runbook with the evidence that identified it.
- Re-run the failing request after the fix, and close the loop in the record.
The official class definitions are worth bookmarking rather than memorizing: Anthropic's errors page, OpenAI's error codes guide, and Gemini's troubleshooting page. Use them to confirm what a class means on the platform you are calling; use this map to decide which class you are in. As always, the official pages current at debug time are the authority.