BerriAI/litellm · error · ValueError

Chat provider: Invalid chunk type

Error message

Chat provider: Invalid chunk type {type(parsed_chunk)}

What it means

After converting BaseModel inputs via model_dump, translate_responses_chunk_to_openai_stream requires the chunk to be a dict. A chunk that is a string (e.g. a raw 'data: {...}' SSE line), a list, or any other type cannot be inspected for type/object/choices fields and is rejected with the actual type in the message. It marks a feeding bug: un-parsed or wrongly-parsed events reaching the translator.

Solutions

  1. Parse every SSE data line with json.loads after stripping the 'data:' prefix, and pass the resulting dict
  2. Check for double-encoded payloads: if json.loads returns a str, json.loads it again
  3. Yield one parsed event per call — never a list or generator of events
  4. Log type(parsed_chunk) at the boundary feeding the translator to catch regressions

Example fix

// before
for line in response.iter_lines():
    out = translate_responses_chunk_to_openai_stream(line)  # line is 'data: {...}' -> ValueError: Chat provider: Invalid chunk type <class 'str'>

// after
import json
for line in response.iter_lines():
    if not line or not line.startswith("data:"):
        continue
    out = translate_responses_chunk_to_openai_stream(json.loads(line[len("data:"):]))
Defensive patterns

Strategy: type-guard

Validate before calling

import json

def parsed_chunk_is_dict(chunk):
    if isinstance(chunk, str):
        chunk = json.loads(chunk.lstrip("data:").strip() if chunk.startswith("data:") else chunk)
    return chunk

Type guard

def is_translatable_chunk(chunk) -> bool:
    return isinstance(chunk, dict) or hasattr(chunk, "model_dump")

Try / catch

try:
    out = translate_responses_chunk_to_openai_stream(chunk)
except ValueError as e:
    if "Invalid chunk type" in str(e):
        import json
        out = translate_responses_chunk_to_openai_stream(json.loads(chunk))  # chunk was a raw JSON string
    else:
        raise

Prevention

When it happens

Trigger: Passing raw SSE text lines instead of parsed JSON objects; double-encoded JSON where json.loads returns a string; forwarding a list of events as a single chunk; custom iterators yielding tuples/objects the translator does not know.

Common situations: Hand-rolled SSE clients that forget to strip the 'data:' prefix or to json.loads; gateways that wrap payloads in quoted strings; test fixtures building chunks as JSON strings; refactors that changed what the upstream iterator yields.

Related errors


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

Appendix: source

Thrown at litellm/completion_extras/litellm_responses_transformation/transformation.py:1228

        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 77b7c6c40c)