Error Handling
Handle AnyPath gateway errors safely.
Error envelope
OpenAI-compatible gateway endpoints return this JSON envelope for public gateway errors:
{
"error": {
"message": "Human-readable error description",
"type": "error_code",
"code": "error_code"
}
}
At runtime type is identical to code. Client logic should branch on HTTP status and error.code, not on prose in message.
Anthropic-compatible and Gemini-compatible endpoints may preserve native protocol error shapes when the failure comes from protocol handling or an upstream provider.
HTTP status classes
| Status | Meaning | Retry |
|---|---|---|
400 | Invalid request body, parameters, or unsupported fields | No; fix the request |
401 | Missing authentication, or an invalid or expired key | No; send a valid key |
403 | Permission issue, suspended account, or spending limit | No; fix access or balance |
404 | Model or resource not available to this key | No; refresh model discovery |
413 | Request body too large | No; reduce input size |
415 | Unsupported content type | No; use supported content type |
429 | Your rate limit, or the suppliers’ capacity for the model, is exhausted; the code says which | Yes; respect Retry-After when present |
502 | Upstream provider error | Usually yes with backoff |
503 | Model unavailable, provider circuit open, or no route currently available | Usually yes with backoff |
504 | Gateway or provider timeout | Usually yes with backoff |
Public error codes
The full registry is maintained in Error Codes. Common codes include:
| Code | Status | Meaning |
|---|---|---|
auth_required | 401 | API key is missing |
auth_invalid | 401 | API key is invalid |
auth_expired | 401 | API key has expired |
spending_limit_exceeded | 403 | Key spending limit or balance gate blocked the request |
model_not_found | 404 | Requested model is not available to the current key |
rate_limit_exceeded | 429 | Your own RPM/TPM limit was exceeded |
supplier_rate_limited | 429 | You are within your limits; the suppliers of the model are at theirs |
unsupported_request_field | 400 | Selected route does not support one or more request fields |
provider_error | 502 | Upstream provider returned an error |
model_unavailable | 503 | Model or route is temporarily unavailable |
provider_timeout | 504 | Upstream provider timed out |
Retry policy
Use bounded retries only for temporary failures:
import random
import time
def retry_delay_seconds(attempt: int, retryAfterHeader: str | None = None) -> float:
if retryAfterHeader:
return float(retryAfterHeader)
return min(30, (2 ** attempt) + random.random())
- Retry
429,502,503, and504with exponential backoff and jitter. - Respect the
Retry-Afterresponse header when it is present. - Do not retry authentication, authorization, spending-limit, validation, or request-size errors without changing the request or configuration.
- Log status,
error.code, request ID, model, and route metadata. Do not log prompts, files, API keys, or raw provider credentials.
Model availability
Call GET /v1/models with the same API key before storing model configuration. If a later request returns model_not_found, refresh the model list for that key and route policy instead of guessing fallback model names.