{"record":{"id":"503313840efd43bc","repo":"BerriAI/litellm","slug":"chunk-is-not-a-string-chunk","errorCode":null,"errorMessage":"chunk is not a string: {chunk}","messagePattern":"chunk is not a string: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"litellm/litellm_core_utils/streaming_handler.py","lineNumber":1371,"sourceCode":"        elif self.custom_llm_provider == \"text-completion-openai\":\n            response_obj = self.handle_openai_text_completion_chunk(chunk)\n            completion_obj[\"content\"] = response_obj[\"text\"]\n            print_verbose(f\"completion obj content: {completion_obj['content']}\")\n            if response_obj[\"is_finished\"]:\n                self.received_finish_reason = response_obj[\"finish_reason\"]\n            if response_obj[\"usage\"] is not None:\n                setattr(\n                    model_response,\n                    \"usage\",\n                    litellm.Usage(\n                        prompt_tokens=response_obj[\"usage\"].prompt_tokens,\n                        completion_tokens=response_obj[\"usage\"].completion_tokens,\n                        total_tokens=response_obj[\"usage\"].total_tokens,\n                    ),\n                )\n        elif self.custom_llm_provider == \"text-completion-codestral\":\n            if not isinstance(chunk, str):\n                raise ValueError(f\"chunk is not a string: {chunk}\")\n            response_obj = cast(\n                dict[str, Any],\n                litellm.CodestralTextCompletionConfig()._chunk_parser(chunk),\n            )\n            completion_obj[\"content\"] = response_obj[\"text\"]\n            print_verbose(f\"completion obj content: {completion_obj['content']}\")\n            if response_obj[\"is_finished\"]:\n                self.received_finish_reason = response_obj[\"finish_reason\"]\n            if \"usage\" in response_obj is not None:\n                _codestral_usage: Final[Usage] = response_obj[\"usage\"]\n                setattr(\n                    model_response,\n                    \"usage\",\n                    litellm.Usage(\n                        prompt_tokens=_codestral_usage.prompt_tokens,\n                        completion_tokens=_codestral_usage.completion_tokens,\n                        total_tokens=_codestral_usage.total_tokens,\n                    ),","sourceCodeStart":1353,"sourceCodeEnd":1389,"githubUrl":"https://github.com/BerriAI/litellm/blob/6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d/litellm/litellm_core_utils/streaming_handler.py#L1353-L1389","documentation":"Codestral text-completion branch (custom_llm_provider 'text-completion-codestral') asserts the chunk is a str before handing it to CodestralTextCompletionConfig._chunk_parser. If the iterator yields bytes or a parsed object, it raises this ValueError.","triggerScenarios":"Using model='text-completion-codestral/<model>' with stream=True when the transport yields bytes (not decoded) or a dict, e.g. an aiohttp/aiohttp-sse wrapper passing raw bytes chunks.","commonSituations":"Custom routers/proxies that forward bytes chunks; version mismatches where an upstream change stopped decoding chunks to str; mocking streams in tests with bytes payloads.","solutions":["Ensure the streaming iterator passed to litellm decodes bytes to str before yielding.","Update litellm — codestral handling has been reworked across versions.","Prefer the standard 'codestral' OpenAI-compatible provider entry point instead of the text-completion variant unless you specifically need raw completion mode."],"exampleFix":"# before (custom generator)\nasync def gen():\n    async for b in resp.content.iter_any():\n        yield b  # bytes\n# after\nasync def gen():\n    async for b in resp.content.iter_any():\n        yield b.decode(\"utf-8\")  # str","handlingStrategy":"type-guard","validationCode":"def ensure_str_chunks(source):\n    for c in source:\n        if isinstance(c, bytes):\n            c = c.decode(\"utf-8\")\n        if not isinstance(c, str):\n            raise TypeError(f\"expected str chunk, got {type(c)}\")\n        yield c","typeGuard":"def is_str_chunk(c) -> bool:\n    return isinstance(c, str)","tryCatchPattern":"try:\n    for part in litellm.completion(model=\"text-completion-codestral/...\", stream=True, ...):\n        ...\nexcept ValueError as e:\n    if \"chunk is not a string\" in str(e):\n        stream = (c.decode(\"utf-8\") if isinstance(c, bytes) else str(c) for c in raw_source)\n        # retry with decoded chunks\n        raise","preventionTips":["Decode transport bytes to str before handing iterators to litellm.","Prefer the OpenAI-compatible codestral provider over the text-completion variant."],"tags":["codestral","mistral","streaming","type-mismatch"],"backgroundTag":null,"analyzedSha":"6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d","analyzedAt":"2026-08-15T07:12:03.035Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}