All articles

LLM API Error Codes Across Providers: A Cross-Provider Reference for 4xx/5xx, Retries, and Edge Cases

A practical cross-provider reference mapping every HTTP error code across OpenAI, Anthropic, DeepSeek, and DashScope APIs. We compared authentication failures, rate-limit flavors, content-filter rejections, model-not-found responses, and streaming mid-stream errors — then built a decision tree for when to retry, when to fall back, and when to fail fast.

· TheRouter

Every LLM API returns errors differently. OpenAI uses four flavors of 429. Anthropic invented 529. DeepSeek has a dedicated 402. DashScope wraps errors in a code/message envelope that sometimes contradicts the HTTP status.

If you route requests across multiple providers, you need a single mental model for all of them. We built this reference for our own routing layer and keep it updated as providers change their error surfaces.

OpenAI-compatible means a provider exposes a chat-completions endpoint whose request and response shape matches the OpenAI API contract closely enough that an unmodified OpenAI SDK call works against it after swapping three values: API key, base URL, and model name. The minimum surface in practice is POST /v1/chat/completions with messages, model, and an OpenAI-shaped streaming response.

TL;DR — Error Code Comparison Table

HTTP StatusError TypeOpenAIAnthropicDeepSeekDashScopeRetry-safe?Recommended Action
400Invalid requestinvalid_request_errorinvalid_request_errorInvalid Format / Invalid Parameters (422)InvalidParameterNoFix request payload
401Authenticationinvalid_authenticationauthentication_errorAuthentication FailsInvalidApiKeyNoCheck API key
402BillingInsufficient BalanceNoTop up account
403Permissioncountry_not_supportedpermission_errorNoCheck access/region
404Not foundnot_found_errorModelNotFoundNoFix model ID
413Too largerequest_too_largeNoReduce input size
429Rate limitrate_limit_error + 3 spend variantsrate_limit_errorRate Limit ReachedThrottling / FlowControlYes (rate); No (spend)Backoff; check Retry-After
500Server errorserver_errorapi_errorServer ErrorInternalErrorYesRetry with backoff
503Overloadedoverloaded / slow_downServer OverloadedServiceUnavailableYesRetry with backoff
529Overloadedoverloaded_errorYesRetry with backoff

Data retrieved July 31, 2026. OpenAI from developers.openai.com/api/docs/guides/error-codes. Anthropic from platform.claude.com/docs/en/api/errors and github.com/anthropics/skills. DeepSeek from api-docs.deepseek.com/quick_start/error_codes. DashScope from alibabacloud.com/help/en/model-studio/error-code.

Authentication Errors: 401 vs 403

Authentication errors are never retryable, but providers signal them differently.

OpenAI returns 401 for four distinct causes: invalid key, incorrect key format, missing organization membership, and IP allowlist violations. They also use 403 for unsupported countries/regions — a geographic restriction, not a credential problem.

Anthropic cleanly separates 401 (authentication — wrong or missing key) from 403 (permission — key lacks access to a specific model or beta feature). The key detail: if you pass an OAuth bearer token via x-api-key instead of Authorization: Bearer, you get a 401, not a 403.

DeepSeek returns 401 for all authentication failures. They don't use 403 at all in their documented error surface.

DashScope returns 400 with code InvalidApiKey for authentication failures — not 401. This is a quirk of their error envelope: the HTTP status is 400, but the semantic error code inside the response body tells you it's an auth problem. If you're parsing HTTP status codes alone, you'll misclassify this as a bad request.

Practical takeaway

Don't rely on HTTP status 401 alone to detect authentication failures across providers. Parse the error body. DashScope's InvalidApiKey arrives as a 400.

Rate-Limit Errors: The Many Flavors of 429

Rate limits are the most complex error category because providers overload 429 for fundamentally different problems.

OpenAI: Four types of 429

OpenAI returns 429 for four distinct causes, each with a different error.code:

  1. rate_limit_reached — you exceeded RPM or TPM. Retryable after the Retry-After header interval.
  2. credit_balance_exhausted — prepaid credits depleted. Not retryable; add credits.
  3. organization_spend_limit_exceeded — org-level spend cap hit. Not retryable; raise the limit.
  4. project_spend_limit_exceeded — project-level spend cap hit. Not retryable; raise the limit.

The critical distinction: only the first type is retryable. The other three require account-level changes. If you retry a spend-limit 429 with exponential backoff, you'll burn client-side resources waiting for something that will never resolve on its own.

Anthropic: Clean 429 + unique 529

Anthropic uses 429 only for rate limits (RPM, ITPM, OTPM). They provide retry-after, x-ratelimit-limit-*, and x-ratelimit-remaining-* headers. Their SDKs auto-retry 429 and 5xx errors with exponential backoff (default: 2 retries).

Anthropic's unique contribution: 529 overloaded_error for capacity saturation. This is distinct from 500 api_error (server bug). Both are retryable, but 529 specifically means "we're busy, try later" — not "something broke."

DeepSeek: 429 for rate limits, 402 for billing

DeepSeek keeps it simple: 429 means rate limit reached, 402 means insufficient balance. No overloaded 429 variants for spend limits. The 402 is a clear signal — stop retrying and top up.

DashScope: Throttling vs FlowControl

DashScope returns 429 with two internal codes: Throttling (you exceeded your per-model QPM/TPM limit) and FlowControl (system-level flow control during high load). Both are retryable, but FlowControl may indicate a longer wait. DashScope also supports temporary limit increases via the console — useful during traffic spikes.

Content-Filter and Safety Errors

Content-filter rejections arrive as 400 errors across all providers, but with different internal codes:

  • OpenAI: 400 with content_filter in the error message. For streaming, a content_filter finish reason in the chunk.
  • Anthropic: 400 invalid_request_error when content violates usage policy. No separate error type — it's bundled with other request validation failures.
  • DeepSeek: 400 Invalid Format with a message indicating content policy violation.
  • DashScope: 400 with code DataInspectionFailed — the most descriptive code of the group.

Content-filter errors are never retryable with the same input. You must modify the request content.

Model-Not-Found and Deprecated Model Errors

When you request a model that doesn't exist or has been deprecated:

  • OpenAI: 404 with a message listing the model ID and suggesting alternatives. After a model's shutdown date, the same model ID returns 404.
  • Anthropic: 404 not_found_error. Common cause: typo in model ID (e.g., claude-sonnet-4.6 instead of claude-sonnet-4-6). Aliases like claude-opus-5 work.
  • DeepSeek: No documented 404 — DeepSeek's model namespace is small and stable.
  • DashScope: 400 with code ModelNotFound or InvalidModel. After a model's sunset date, requests return this error. DashScope also returns InvalidModel when you call a model with the wrong API endpoint (e.g., text model via the image generation endpoint).

Graceful degradation for deprecated models

If you're routing through multiple providers, model-not-found errors should trigger a fallback to an equivalent model on the same or different provider — not a retry. See our fallback routing guide for patterns.

Streaming Mid-Stream Errors

Streaming (SSE) introduces a subtle problem: the HTTP connection returns 200 on initial handshake, but errors can occur mid-stream after you've already started processing chunks.

  • OpenAI: May send an error event inside the SSE stream. The initial HTTP status is 200, so you must check each chunk for error events. The stream may also simply disconnect without a final [DONE] marker during server errors.
  • Anthropic: Sends a content_block_stop or message_stop event on success, and an error event type on failure. Their streaming protocol is event-typed, making error detection cleaner than OpenAI's format.
  • DeepSeek: Follows the OpenAI-compatible SSE format. Mid-stream errors appear as error chunks, and disconnections may occur without [DONE].
  • DashScope: When using the OpenAI-compatible endpoint, follows the same SSE error pattern as OpenAI. The native DashScope protocol has its own event format.

For more on streaming implementation differences, see our cross-provider streaming guide.

Practical takeaway

Never assume a 200 HTTP status means the entire response succeeded. Always implement stream-level error handlers.

Gateway Error Normalization

When routing requests across providers, diverse error formats create a real problem: your client code needs N different error handlers for N providers. A routing gateway should normalize upstream errors into a consistent downstream contract.

We suggest mapping to these error classes:

Downstream ClassMaps FromRetryableAction
auth_error401, 403, DashScope InvalidApiKeyNoFix credentials
invalid_request400 (except content filter), 413, 422NoFix request
content_filtered400 with content policy flagNoModify content
rate_limited429 (rate variants only)YesBackoff + retry
billing_error402, 429 (spend/credit variants)NoTop up / raise limit
model_unavailable404, ModelNotFoundNoFallback to alt model
provider_error500, 503, 529YesRetry, then fallback

Preserve the upstream provider's error code and message in a metadata field. Developers debugging a production issue need to see the original credit_balance_exhausted code, not just "billing_error."

TheRouter routes OpenAI-compatible requests across configured providers and supports model fallback when live product paths support it. Error normalization is part of the routing contract — upstream 429s and 5xx errors trigger fallback chains when configured.

Decision Tree: Retry vs Fallback vs Fail

Error received
├── Is HTTP status 429?
│   ├── Is error.code a spend/credit/billing code?
│   │   └── FAIL — not retryable; requires account action
│   └── Is error.code a rate-limit code?
│       ├── Is Retry-After header present?
│       │   └── WAIT the specified duration, then RETRY same provider
│       └── No Retry-After
│           └── RETRY with exponential backoff (max 3 attempts)
│               └── Still failing? → FALLBACK to next provider
├── Is HTTP status 500, 503, or 529?
│   └── RETRY with exponential backoff (max 3 attempts)
│       └── Still failing? → FALLBACK to next provider
├── Is HTTP status 400 (content filter)?
│   └── FAIL — content must be modified; fallback won't help
├── Is HTTP status 400, 401, 403, 404, 413, or 422?
│   └── FAIL — client-side problem; fix the request
└── Is HTTP status 402?
    └── FAIL — billing problem; top up account

Frequently Asked Questions

Which LLM API errors are safe to retry?

HTTP 429 (rate limit only — not spend/credit variants), 500 (server error), 503 (overloaded), and 529 (Anthropic overloaded) are generally safe to retry with exponential backoff. All 400-level errors except rate-limit 429 require client-side changes. See the rate-limit comparison for provider-specific limits.

Why does OpenAI return different types of 429 errors?

OpenAI uses 429 for at least four distinct causes: RPM/TPM rate limits, credit balance exhaustion, organization spend limits, and project spend limits. Check the error.code field to distinguish between rate_limit_reached, credit_balance_exhausted, organization_spend_limit_exceeded, and project_spend_limit_exceeded. Only rate_limit_reached is retryable.

What is Anthropic's 529 error code?

Anthropic returns HTTP 529 (overloaded_error) when the API is temporarily at capacity. This is distinct from their 500 (api_error) which indicates a server-side bug. Both are retryable with exponential backoff. If 529 persists, consider routing to a less-loaded model — Haiku-class models tend to have more available capacity.

How does DeepSeek handle insufficient balance differently from OpenAI?

DeepSeek returns HTTP 402 (Insufficient Balance) as a dedicated status code, while OpenAI overloads 429 with a credit_balance_exhausted error code. DeepSeek's approach is arguably clearer — a 402 is never retryable and always means you need to top up.

Do LLM APIs signal errors differently during SSE streaming?

Yes. All providers may return HTTP 200 for the initial SSE connection but then send error events mid-stream. Always implement stream-level error handlers — don't assume a 200 means the full response will succeed. See our streaming implementation guide for code patterns.

How should a routing gateway normalize errors across providers?

Map upstream errors to a consistent downstream contract: classify each error as auth_error, invalid_request, rate_limited, content_filtered, model_unavailable, or provider_error. Preserve the upstream error code and message in metadata for debugging. This lets clients write one retry/fallback handler regardless of which provider served the request. See our gateway comparison for how different routing solutions approach this.

Where can I check provider status pages during outages?

Customer Support