BerriAI/litellm · error · ValueError
Chat provider: Invalid chunk type {type(parsed_chunk)}
Error message
Chat provider: Invalid chunk type {type(parsed_chunk)} What it means
The chunk transformer raises when parsed_chunk is not a dict after BaseModel normalization — i.e. not a pydantic model, not a dict (e.g. a str, bytes, or arbitrary object). Events must arrive as deserialized objects; raw text frames or unparsed bytes are rejected.
Source
Thrown at litellm/completion_extras/litellm_responses_transformation/transformation.py:1226
Raises:
ValueError: If chunk is invalid or missing required fields
"""
from litellm.types.llms.openai import ChatCompletionToolCallFunctionChunk
from litellm.types.utils import (
ChatCompletionToolCallChunk,
Delta,
ModelResponseStream,
StreamingChoices,
)
if not parsed_chunk:
raise ValueError("Chat provider: Empty parsed_chunk")
if isinstance(parsed_chunk, BaseModel):
parsed_chunk = parsed_chunk.model_dump()
if not isinstance(parsed_chunk, dict):
raise ValueError(f"Chat provider: Invalid chunk type {type(parsed_chunk)}")
# Handle different event types from responses API
event_type = parsed_chunk.get("type")
if isinstance(event_type, ResponsesAPIStreamEvents):
event_type = event_type.value
if parsed_chunk.get("object") == "chat.completion.chunk" or (
event_type is None and isinstance(parsed_chunk.get("choices"), list) and parsed_chunk.get("choices")
):
return ModelResponseStream(**parsed_chunk)
verbose_logger.debug("Chat provider: Processing event type: %s", event_type)
if event_type == "response.created":
# Initial response creation event
verbose_logger.debug("Chat provider: response.created -> %s", parsed_chunk)
return ModelResponseStream(
choices=[View on GitHub (pinned to 6c2dcb801b)
Solutions
- json.loads each SSE data payload before handing it to the transformer
- Skip non-data frames (event:, id:, comments) in your SSE reader
- Let litellm's built-in streaming handle deserialization when possible
Example fix
# before
async for line in response.aiter_lines():
if line.startswith("data: "):
out = transformer.chunk_parser(line[6:]) # raw str
# after
import json
async for line in response.aiter_lines():
if line.startswith("data: "):
payload = line[6:]
if payload == "[DONE]":
break
out = transformer.chunk_parser(json.loads(payload)) Defensive patterns
Strategy: type-guard
Validate before calling
import json
def is_parsed_event(chunk) -> bool:
if isinstance(chunk, str):
try:
json.loads(chunk)
return False # still a string: parse it first
except json.JSONDecodeError:
return False
return isinstance(chunk, dict) Type guard
def is_event_dict(v) -> bool:
return isinstance(v, dict) Try / catch
for raw in sse_lines:
payload = json.loads(raw[6:])
if not isinstance(payload, dict):
continue
out = transformer.chunk_parser(payload, ...) Prevention
- Always json.loads SSE data payloads before transformation
- Never forward raw lines, bytes, or serialized JSON strings to chunk parsers
When it happens
Trigger: Passing raw SSE lines (strings) or bytes from httpx directly into chunk_parser; a custom deserializer returning json.dumps(...) output instead of json.loads(...) result.
Common situations: Hand-rolled SSE clients that forget json.loads on the data: payload; mixing sync/async iterators and accidentally yielding the iterator object; double-encoded JSON strings.
Related errors
- Chat provider: Empty parsed_chunk
- Chat provider: Invalid function argument delta {parsed_chunk
- Chat provider: Invalid text delta {parsed_chunk}
- Braintrust API error: {e.response.text}
- Failed to connect to Braintrust API: {str(e)}
AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15).
Data as JSON: /api/errors/753c4912bbf4cade.
Report an issue: GitHub.