BerriAI/litellm · error · APIError
APIError
Error message
APIError: {exception_provider} - {error_str} What it means
Catch-all of _map_openrouter_exception: any OpenRouter status not in {400,401,404,408,422,429,503,504} becomes litellm.APIError, preserving status_code, the request (when present), model, and debug info. The message 'APIError: <provider> - <error_str>' still carries OpenRouter's raw error text.
Solutions
- Branch on e.status_code: 402 means billing/credits on the OpenRouter account; 5xx means retryable upstream trouble.
- Upgrade litellm to pick up any newly added specific mappings.
- For 402: add credits or switch keys; for 5xx: retry with backoff or failover to another provider.
- Report unmapped recurring statuses with the response payload to the litellm repo.
Example fix
# before
litellm.completion(model="openrouter/x", messages=msgs)
# after
try:
litellm.completion(model="openrouter/x", messages=msgs)
except litellm.APIError as e:
if e.status_code == 402:
raise RuntimeError("OpenRouter account out of credits") from e
if e.status_code and e.status_code >= 500:
raise # retryable upstream
raise Defensive patterns
Strategy: try-catch
Type guard
def is_payment_error(e: Exception) -> bool:
return isinstance(e, litellm.APIError) and getattr(e, "status_code", None) == 402 Try / catch
try:
resp = litellm.completion(model="openrouter/...", **kwargs)
except litellm.APIError as e:
if getattr(e, "status_code", 0) >= 500:
retry_with_backoff()
else:
raise # 402/403 etc. need human action, not retries Prevention
- Branch on status_code, not on message text, for unmapped OpenRouter errors.
- Monitor for 402 specifically — it's the out-of-credit signal.
- Keep litellm updated so unmapped codes shrink over time.
When it happens
Trigger: Uncommon OpenRouter statuses such as 402 (payment/credit required), 403, 405, 410, or 500; any newly introduced status litellm doesn't specifically map yet.
Common situations: Out-of-credit accounts surfacing 402; new OpenRouter status codes after API changes; older litellm versions lacking newer mappings; upstream 500s from provider errors.
Related errors
- AzureException APIError
- APIConnectionError
- ReplicateException
- APITimeoutError - Request timed out. Error_str
- BFL error
AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18).
Data as JSON: /api/errors/f67425574d591298.
Report an issue: GitHub.
Appendix: source
Thrown at litellm/litellm_core_utils/exception_mapping_utils.py:2145
)
elif original_exception.status_code == 503:
raise ServiceUnavailableError(
message=f"ServiceUnavailableError: {exception_provider} - {error_str}",
model=model,
llm_provider=custom_llm_provider,
response=getattr(original_exception, "response", None),
litellm_debug_info=extra_information,
)
elif original_exception.status_code == 504: # gateway timeout error
raise Timeout(
message=f"Timeout Error: {exception_provider} - {error_str}",
model=model,
llm_provider=custom_llm_provider,
litellm_debug_info=extra_information,
exception_status_code=original_exception.status_code,
)
else:
raise APIError(
status_code=original_exception.status_code,
message=f"APIError: {exception_provider} - {error_str}",
llm_provider=custom_llm_provider,
model=model,
request=getattr(original_exception, "request", None),
litellm_debug_info=extra_information,
)
else:
# if no status code then it is an APIConnectionError: https://github.com/openai/openai-python#handling-errors
raise APIConnectionError(
message=f"APIConnectionError: {exception_provider} - {error_str}",
llm_provider=custom_llm_provider,
model=model,
litellm_debug_info=extra_information,
request=httpx.Request(method="POST", url="https://api.openai.com/v1/"),
)
View on GitHub (pinned to 77b7c6c40c)