BerriAI/litellm · error · MaskedHTTPStatusError

_body (masked response body)

Error message

_body (masked response body)

What it means

When a sync streaming HTTP call in litellm's HTTPHandler gets a non-2xx response, litellm reads the body (with a bounded timeout), masks sensitive info such as keys in the URL/body via mask_sensitive_info, and raises MaskedHTTPStatusError. The message is the masked response body of the failed upstream request.

Source

Thrown at litellm/llms/custom_httpx/http_handler.py:460

            except Exception:
                response.close()
                return b""
        return response.read()
    except Exception:
        return b""


def _raise_masked_sync_error(e: httpx.HTTPStatusError, stream: bool) -> None:
    """Raise a MaskedHTTPStatusError for sync HTTP handlers."""
    if stream:
        try:
            _body: Final = mask_sensitive_info(
                _safe_read_response(
                    e.response,
                    timeout=_STREAMING_ERROR_BODY_READ_TIMEOUT_SECONDS,
                )
            )
            raise MaskedHTTPStatusError(e, message=_body, text=_body) from None
        finally:
            try:
                e.response.close()
            except Exception:
                pass
    _text: Final = mask_sensitive_info(_safe_get_response_text(e.response))
    raise MaskedHTTPStatusError(e, message=_text, text=_text) from None


async def _raise_masked_async_error(e: httpx.HTTPStatusError, stream: bool) -> None:
    """Raise a MaskedHTTPStatusError for async HTTP handlers."""
    if stream:
        try:
            _body: Final = mask_sensitive_info(
                await _safe_aread_response(
                    e.response,
                    timeout=_STREAMING_ERROR_BODY_READ_TIMEOUT_SECONDS,
                )

View on GitHub (pinned to 6c2dcb801b)

Solutions

  1. Catch httpx.HTTPStatusError (MaskedHTTPStatusError subclasses it) and inspect .response.status_code
  2. Fix the root cause per status: 401/403 -> API key; 404 -> model name/api_base; 429 -> backoff or higher limits; 5xx -> provider health
  3. If behind a proxy, verify it forwards auth headers and returns the provider's real status

Example fix

# before
resp = client.post(url, json=payload)  # raises on non-2xx

# after
import httpx
try:
    resp = client.post(url, json=payload)
except httpx.HTTPStatusError as e:  # includes MaskedHTTPStatusError
    if e.response.status_code == 429:
        await asyncio.sleep(backoff)
    else:
        raise
Defensive patterns

Strategy: retry

Try / catch

import httpx
from litellm.llms.custom_httpx.http_handler import MaskedHTTPStatusError
try:
    stream = client.stream("POST", url, ...)
except MaskedHTTPStatusError as e:
    if e.response.status_code == 429:
        retry_with_backoff()
    raise

Prevention

When it happens

Trigger: Sync streaming completions where the upstream returns 4xx/5xx — invalid API key (401), model not found, context length exceeded, rate limits (429), or upstream 5xx — during httpx streaming.

Common situations: Wrong or expired API keys; hitting provider rate limits mid-stream; requesting a model name the provider does not serve; proxy servers returning HTML/JSON error pages. Masking means the exception text is safe to log.

Related errors


AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15). Data as JSON: /api/errors/a2633e140bbc1efd. Report an issue: GitHub.