run-llama/llama_index · error · ValueError

Invalid message content: {message.content!s}

Error message

Invalid message content: {message.content!s}

What it means

Raised by BaseLLM.convert_chat_messages when a ChatMessage's content is neither a str nor a List (of blocks). The message content contract is string or block-list; anything else (int, dict, None, custom object) hits this branch with the offending value echoed via !s.

Source

Thrown at llama-index-core/llama_index/core/base/llms/base.py:84

        return {"class_name": self.class_name(), **self.metadata.model_dump()}

    def convert_chat_messages(self, messages: Sequence[ChatMessage]) -> List[Any]:
        """Convert chat messages to an LLM specific message format."""
        converted_messages = []
        for message in messages:
            if isinstance(message.content, str):
                converted_messages.append(message)
            elif isinstance(message.content, List):
                content_string = ""
                for block in message.content:
                    if isinstance(block, TextBlock):
                        content_string += block.text
                    else:
                        raise ValueError("LLM only supports text inputs")
                message.content = content_string
                converted_messages.append(message)
            else:
                raise ValueError(f"Invalid message content: {message.content!s}")

        return converted_messages

    @abstractmethod
    def chat(self, messages: Sequence[ChatMessage], **kwargs: Any) -> ChatResponse:
        """
        Chat endpoint for LLM.

        Args:
            messages (Sequence[ChatMessage]):
                Sequence of chat messages.
            kwargs (Any):
                Additional keyword arguments to pass to the LLM.

        Returns:
            ChatResponse: Chat response from the LLM.

        Examples:

View on GitHub (pinned to afd0fef371)

Solutions

  1. Ensure every ChatMessage content is a str: coerce with str(...) at construction.
  2. For rich content, build a list of typed blocks: [TextBlock(text=...), ImageBlock(...)].
  3. Validate messages before calling chat (see type guard below) and reject/normalize bad ones at your boundary.

Example fix

# before
msg = ChatMessage(role=MessageRole.USER, content=payload["text"] if "text" in payload else None)

# after
text = str(payload.get("text", ""))
msg = ChatMessage(role=MessageRole.USER, content=text)
Defensive patterns

Strategy: type-guard

Validate before calling

msgs = [m if isinstance(m.content, (str, list)) and m.content else m.model_copy(update={"content": str(m.content or "")}) for m in msgs]

Type guard

def is_valid_content(c: Any) -> bool:
    return isinstance(c, (str, list))

Prevention

When it happens

Trigger: Calling llm.chat with ChatMessage(content=123), content=None (non-defaulted), content={"text": ...}, or any non-str/non-list object; programmatic message builders that pass through unvalidated payloads.

Common situations: Passing parsed JSON or numbers from an API straight into ChatMessage; a None content slipping through when optional fields are forwarded; refactors changing content from str to dict.

Related errors


AI-assisted analysis of run-llama/llama_index@afd0fef371 (2026-08-15). Data as JSON: /api/errors/e016da976e1a8d24. Report an issue: GitHub.