{"record":{"id":"753c4912bbf4cade","repo":"BerriAI/litellm","slug":"chat-provider-invalid-chunk-type-type-parsed-chu","errorCode":null,"errorMessage":"Chat provider: Invalid chunk type {type(parsed_chunk)}","messagePattern":"Chat provider: Invalid chunk type (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"litellm/completion_extras/litellm_responses_transformation/transformation.py","lineNumber":1228,"sourceCode":"\n        Raises:\n            ValueError: If chunk is invalid or missing required fields\n        \"\"\"\n        from litellm.types.llms.openai import ChatCompletionToolCallFunctionChunk\n        from litellm.types.utils import (\n            ChatCompletionToolCallChunk,\n            Delta,\n            ModelResponseStream,\n            StreamingChoices,\n        )\n\n        if not parsed_chunk:\n            raise ValueError(\"Chat provider: Empty parsed_chunk\")\n\n        if isinstance(parsed_chunk, BaseModel):\n            parsed_chunk = parsed_chunk.model_dump()\n        if not isinstance(parsed_chunk, dict):\n            raise ValueError(f\"Chat provider: Invalid chunk type {type(parsed_chunk)}\")\n\n        # Handle different event types from responses API\n        event_type = parsed_chunk.get(\"type\")\n        if isinstance(event_type, ResponsesAPIStreamEvents):\n            event_type = event_type.value\n\n        if parsed_chunk.get(\"object\") == \"chat.completion.chunk\" or (\n            event_type is None and isinstance(parsed_chunk.get(\"choices\"), list) and parsed_chunk.get(\"choices\")\n        ):\n            return ModelResponseStream(**parsed_chunk)\n\n        verbose_logger.debug(\"Chat provider: Processing event type: %s\", event_type)\n\n        if event_type == \"response.created\":\n            # Initial response creation event\n            verbose_logger.debug(\"Chat provider: response.created -> %s\", parsed_chunk)\n            return ModelResponseStream(\n                choices=[","sourceCodeStart":1210,"sourceCodeEnd":1246,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/completion_extras/litellm_responses_transformation/transformation.py#L1210-L1246","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Parse every SSE data line with json.loads after stripping the 'data:' prefix, and pass the resulting dict","Check for double-encoded payloads: if json.loads returns a str, json.loads it again","Yield one parsed event per call — never a list or generator of events","Log type(parsed_chunk) at the boundary feeding the translator to catch regressions"],"exampleFix":"// before\nfor line in response.iter_lines():\n    out = translate_responses_chunk_to_openai_stream(line)  # line is 'data: {...}' -> ValueError: Chat provider: Invalid chunk type <class 'str'>\n\n// after\nimport json\nfor line in response.iter_lines():\n    if not line or not line.startswith(\"data:\"):\n        continue\n    out = translate_responses_chunk_to_openai_stream(json.loads(line[len(\"data:\"):]))","handlingStrategy":"type-guard","validationCode":"import json\n\ndef parsed_chunk_is_dict(chunk):\n    if isinstance(chunk, str):\n        chunk = json.loads(chunk.lstrip(\"data:\").strip() if chunk.startswith(\"data:\") else chunk)\n    return chunk","typeGuard":"def is_translatable_chunk(chunk) -> bool:\n    return isinstance(chunk, dict) or hasattr(chunk, \"model_dump\")","tryCatchPattern":"try:\n    out = translate_responses_chunk_to_openai_stream(chunk)\nexcept ValueError as e:\n    if \"Invalid chunk type\" in str(e):\n        import json\n        out = translate_responses_chunk_to_openai_stream(json.loads(chunk))  # chunk was a raw JSON string\n    else:\n        raise","preventionTips":["Strip 'data:' prefixes and json.loads every SSE line before translation","Guard against double-encoded JSON (json.loads returning str) at the parse boundary","Log type(chunk) where streams enter your code to catch shape regressions early"],"tags":["streaming","sse","type-validation","responses-api"],"backgroundTag":"invalid-chunk-type","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}