BerriAI/litellm · error · ValueError
Failed to convert ModelResponse to ModelResponseStream: {mod
Error message
Failed to convert ModelResponse to ModelResponseStream: {model_response}. Error: {e} What it means
When a provider returns a complete (non-streaming) ModelResponse that LiteLLM must fake as a stream, the generic converter builds a chat.completion.chunk from the response's fields. If any field access or pydantic construction fails (malformed choices, missing attributes on a partial response), the whole conversion is caught and re-raised with the serialized response for debugging.
Source
Thrown at litellm/llms/base_llm/base_model_iterator.py:63
),
finish_reason=choice.finish_reason,
)
)
processed_chunk: Final = ModelResponseStream(
id=model_response.id,
object="chat.completion.chunk",
created=model_response.created,
model=model_response.model,
choices=streaming_choices,
)
# Carry usage onto the streaming chunk so fake-streamed responses
# (e.g. Vertex AI Gemma :predict) still report token counts.
usage: Final = getattr(model_response, "usage", None)
if usage is not None:
setattr(processed_chunk, "usage", usage)
return processed_chunk
except Exception as e:
raise ValueError(f"Failed to convert ModelResponse to ModelResponseStream: {model_response}. Error: {e}")
class BaseModelResponseIterator:
def __init__(self, streaming_response, sync_stream: bool, json_mode: bool | None = False):
self.streaming_response = streaming_response
self.response_iterator = self.streaming_response
self.json_mode = json_mode
self.http_response: httpx.Response | None = None
async def aclose(self) -> None:
"""Close the upstream HTTP response so the provider connection is
released (and a backend like vLLM aborts generation) when the stream
is abandoned before its natural end.
``streaming_response`` is usually a bare ``aiter_lines()`` generator
that holds no reference to the response, so the handler that owns the
response attaches it here after construction."""
if self.http_response is not None:View on GitHub (pinned to 6c2dcb801b)
Solutions
- Inspect the printed model_response in the message — usually a malformed choices list is visible
- If building ModelResponse yourself, construct it via ModelResponse(choices=[Choices(delta=..., message=...)]) so fields are well-formed
- Call with stream=False to see the raw response error directly instead of the conversion failure
- Upgrade litellm — conversion helpers gain field tolerance over versions
Example fix
# before (custom handler)
resp = ModelResponse(); resp.choices = [{}] # malformed
# after
from litellm.types.utils import ModelResponse, Choices, Delta
resp = ModelResponse(choices=[Choices(index=0, delta=Delta(content='hi'), finish_reason='stop')]) Defensive patterns
Strategy: try-catch
Try / catch
try:
for chunk in response:
process(chunk)
except ValueError as e:
if 'Failed to convert ModelResponse to ModelResponseStream' in str(e):
log.error('malformed provider response: %s', e)
# fall back to non-streaming call
else:
raise Prevention
- When building custom ModelResponse objects, always use the pydantic constructors (Choices/Delta) rather than assigning raw dicts
- Test custom provider adapters with stream=True and stream=False in CI
When it happens
Trigger: A provider handler returns a ModelResponse whose choices/usage are malformed or whose types don't fit Choice chunk construction (e.g. custom provider adapter or a mocked response), and stream=True forces the conversion path.
Common situations: Writing/testing a custom provider integration that returns hand-built ModelResponse objects; a mocked response object missing expected attributes; version mismatches between litellm core and a provider adapter after upgrade.
Related errors
- Unexpected responses stream payload
- Braintrust API error: {e.response.text}
- Failed to connect to Braintrust API: {str(e)}
- Failed to parse Braintrust API response: {str(e)}
- api_base is required for Pydantic AI agents
AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15).
Data as JSON: /api/errors/9f854ba90da6cb90.
Report an issue: GitHub.