BerriAI/litellm · error · OpenAIError

{message}

Error message

{message}

What it means

Final catch-all of the async acompletion flow (litellm/llms/openai/openai.py:947). Mirrors the sync wrapper: exceptions that are not OpenAIError are converted into one, carrying the upstream status_code (default 500), headers (taken from e.headers, falling back to e.response.headers), body, and a message read from e.message or str(e). The wrapper exists because exceptions raised inside coroutines must be normalized before they cross back into litellm's plumbing.

Source

Thrown at litellm/llms/openai/openai.py:947

                return final_response_obj
            except openai.UnprocessableEntityError as e:
                ## check if body contains unprocessable params - related issue https://github.com/BerriAI/litellm/issues/4800
                if litellm.drop_params is True or drop_params is True:
                    data = drop_params_from_unprocessable_entity_error(e, data)
                else:
                    raise e
                # e.message
            except Exception as e:
                exception_response = getattr(e, "response", None)
                status_code = getattr(e, "status_code", 500)
                exception_body = getattr(e, "body", None)
                error_headers = getattr(e, "headers", None)
                if error_headers is None and exception_response:
                    error_headers = getattr(exception_response, "headers", None)
                message = getattr(e, "message", str(e))

                raise OpenAIError(
                    status_code=status_code,
                    message=message,
                    headers=error_headers,
                    body=exception_body,
                )

    def streaming(
        self,
        logging_obj,
        timeout: float | httpx.Timeout,
        data: dict,
        model: str,
        api_key: str | None = None,
        api_base: str | None = None,
        api_version: str | None = None,
        organization: str | None = None,
        client=None,
        max_retries=None,

View on GitHub (pinned to 77b7c6c40c)

Solutions

  1. Inspect e.status_code and e.message to classify: auth vs rate-limit vs context-window vs 5xx
  2. For context-length errors, trim messages or move to a larger-context model
  3. Configure num_retries and litellm.Router fallbacks for 429/5xx
  4. For auth errors rotate the key — retrying will not help
Defensive patterns

Strategy: try-catch

Try / catch

import asyncio
from litellm.llms.openai.common_utils import OpenAIError

async def completion_with_backoff(model, msgs, attempts=3):
    for i in range(attempts):
        try:
            return await litellm.acompletion(model=model, messages=msgs)
        except OpenAIError as e:
            if e.status_code == 429 and i < attempts - 1:
                await asyncio.sleep(2 ** i)
                continue
            if e.status_code >= 500 and i < attempts - 1:
                await asyncio.sleep(1)
                continue
            raise

Prevention

When it happens

Trigger: Async completions failing upstream: 400 context_length_exceeded, 429 rate limit, 401 auth, provider 5xx; asyncio/httpx transport errors surfaced as synthetic 500s carrying the transport message.

Common situations: Async fan-out workers tripping rate limits; expired keys in long-lived async clients; prompts exceeding the model context window; provider regional incidents returning 5xx.

Related errors


AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18). Data as JSON: /api/errors/70f3f2c78912b649. Report an issue: GitHub.