BerriAI/litellm · error · ValueError

chunk is not a string: {chunk}

Error message

chunk is not a string: {chunk}

What it means

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.

Source

Thrown at litellm/litellm_core_utils/streaming_handler.py:1371

        elif self.custom_llm_provider == "text-completion-openai":
            response_obj = self.handle_openai_text_completion_chunk(chunk)
            completion_obj["content"] = response_obj["text"]
            print_verbose(f"completion obj content: {completion_obj['content']}")
            if response_obj["is_finished"]:
                self.received_finish_reason = response_obj["finish_reason"]
            if response_obj["usage"] is not None:
                setattr(
                    model_response,
                    "usage",
                    litellm.Usage(
                        prompt_tokens=response_obj["usage"].prompt_tokens,
                        completion_tokens=response_obj["usage"].completion_tokens,
                        total_tokens=response_obj["usage"].total_tokens,
                    ),
                )
        elif self.custom_llm_provider == "text-completion-codestral":
            if not isinstance(chunk, str):
                raise ValueError(f"chunk is not a string: {chunk}")
            response_obj = cast(
                dict[str, Any],
                litellm.CodestralTextCompletionConfig()._chunk_parser(chunk),
            )
            completion_obj["content"] = response_obj["text"]
            print_verbose(f"completion obj content: {completion_obj['content']}")
            if response_obj["is_finished"]:
                self.received_finish_reason = response_obj["finish_reason"]
            if "usage" in response_obj is not None:
                _codestral_usage: Final[Usage] = response_obj["usage"]
                setattr(
                    model_response,
                    "usage",
                    litellm.Usage(
                        prompt_tokens=_codestral_usage.prompt_tokens,
                        completion_tokens=_codestral_usage.completion_tokens,
                        total_tokens=_codestral_usage.total_tokens,
                    ),

View on GitHub (pinned to 6c2dcb801b)

Solutions

  1. Ensure the streaming iterator passed to litellm decodes bytes to str before yielding.
  2. Update litellm — codestral handling has been reworked across versions.
  3. Prefer the standard 'codestral' OpenAI-compatible provider entry point instead of the text-completion variant unless you specifically need raw completion mode.

Example fix

# before (custom generator)
async def gen():
    async for b in resp.content.iter_any():
        yield b  # bytes
# after
async def gen():
    async for b in resp.content.iter_any():
        yield b.decode("utf-8")  # str
Defensive patterns

Strategy: type-guard

Validate before calling

def ensure_str_chunks(source):
    for c in source:
        if isinstance(c, bytes):
            c = c.decode("utf-8")
        if not isinstance(c, str):
            raise TypeError(f"expected str chunk, got {type(c)}")
        yield c

Type guard

def is_str_chunk(c) -> bool:
    return isinstance(c, str)

Try / catch

try:
    for part in litellm.completion(model="text-completion-codestral/...", stream=True, ...):
        ...
except ValueError as e:
    if "chunk is not a string" in str(e):
        stream = (c.decode("utf-8") if isinstance(c, bytes) else str(c) for c in raw_source)
        # retry with decoded chunks
        raise

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15). Data as JSON: /api/errors/503313840efd43bc. Report an issue: GitHub.