BerriAI/litellm · error · HTTPException

Braintrust API error: {e.response.text}

Error message

Braintrust API error: {e.response.text}

What it means

Raised during iteration of a sync text-completion stream in LiteLLM's OpenAI handler. Even after the stream is established, exceptions can occur while yielding parsed chunks (malformed SSE events, mid-stream connection resets, provider aborts). The for-loop over CustomStreamWrapper catches these and re-wraps them as OpenAIError with status, headers, and text. 'error_text' as the message means the underlying exception lacked a .text attribute and fell back to str(e).

Source

Thrown at cookbook/litellm_proxy_server/braintrust_prompt_wrapper_server.py:211

        )

    # Call Braintrust API
    braintrust_url = f"https://api.braintrust.dev/v1/prompt/{prompt_id}"
    headers = {
        "Authorization": f"Bearer {braintrust_token}",
        "Accept": "application/json",
    }
    print(f"headers: {headers}")
    print(f"braintrust_url: {braintrust_url}")
    print(f"braintrust_token: {braintrust_token}")

    try:
        async with httpx.AsyncClient(timeout=30.0) as client:
            response = await client.get(braintrust_url, headers=headers)
            response.raise_for_status()
            braintrust_data = response.json()
    except httpx.HTTPStatusError as e:
        raise HTTPException(
            status_code=e.response.status_code,
            detail=f"Braintrust API error: {e.response.text}",
        )
    except httpx.RequestError as e:
        raise HTTPException(
            status_code=502,
            detail=f"Failed to connect to Braintrust API: {str(e)}",
        )
    except json.JSONDecodeError as e:
        raise HTTPException(
            status_code=502,
            detail=f"Failed to parse Braintrust API response: {str(e)}",
        )

    print(f"braintrust_data: {braintrust_data}")
    # Transform the response
    try:
        transformed_data = transform_braintrust_response(braintrust_data)

View on GitHub (pinned to 6c2dcb801b)

Solutions

  1. Wrap the entire chunk-consuming loop (not just the call) in try/except for OpenAIError.
  2. Enable num_retries and note that mid-stream retries restart the request; accumulate partial content and decide whether to re-request.
  3. Set explicit stream_timeout to fail fast on stalled streams.
  4. If mid-stream aborts recur, check intermediary proxies for idle-connection timeouts and enable heartbeat/keep-alive.

Example fix

# before
for chunk in litellm.text_completion(model=..., prompt=..., stream=True):
    process(chunk)

# after
try:
    for chunk in litellm.text_completion(model=..., prompt=..., stream=True):
        process(chunk)
except litellm.exceptions.OpenAIError as e:
    logger.error("stream failed: %s (%s)", e.message, e.status_code)
Defensive patterns

Strategy: try-catch

Type guard

from litellm.exceptions import OpenAIError

def is_midstream_failure(e: BaseException) -> bool:
    return isinstance(e, OpenAIError)

Try / catch

from litellm.exceptions import OpenAIError

partial = []
try:
    for chunk in litellm.text_completion(model=..., prompt=..., stream=True):
        partial.append(chunk.choices[0].get("text", ""))
except OpenAIError as e:
    logger.warning("stream died after %d chars: %s", len("".join(partial)), e.message)
    raise

Prevention

When it happens

Trigger: Streaming with stream=True when the connection drops mid-stream, the provider sends an error event partway through, or a chunk fails schema parsing inside CustomStreamWrapper. Also triggered if the stream contains non-JSON SSE data lines.

Common situations: Long-running streams over unstable networks; providers that abort generation on content policy triggers; load balancers killing idle SSE connections; partial responses after long generation times.

Related errors


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