{"record":{"id":"3fe79f5163e7f137","repo":"run-llama/llama_index","slug":"chatmessage-contains-multiple-blocks-use-chatmes","errorCode":null,"errorMessage":"ChatMessage contains multiple blocks, use 'ChatMessage.blocks' instead.","messagePattern":"ChatMessage contains multiple blocks, use 'ChatMessage\\.blocks' instead\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"llama-index-core/llama_index/core/base/llms/types.py","lineNumber":1233,"sourceCode":"        if ct is None and len(content_strs) == 1:\n            return \"\"\n        return ct\n\n    @content.setter\n    def content(self, content: str) -> None:\n        \"\"\"\n        Keeps backward compatibility with the old `content` field.\n\n        Raises:\n            ValueError: if blocks contains more than a block, or a block that's not TextBlock.\n\n        \"\"\"\n        if not self.blocks:\n            self.blocks = [TextBlock(text=content)]\n        elif len(self.blocks) == 1 and isinstance(self.blocks[0], TextBlock):\n            self.blocks = [TextBlock(text=content)]\n        else:\n            raise ValueError(\n                \"ChatMessage contains multiple blocks, use 'ChatMessage.blocks' instead.\"\n            )\n\n    def __str__(self) -> str:\n        return f\"{self.role.value}: {self.content}\"\n\n    @classmethod\n    def from_str(\n        cls,\n        content: str,\n        role: Union[MessageRole, str] = MessageRole.USER,\n        **kwargs: Any,\n    ) -> Self:\n        if isinstance(role, str):\n            role = MessageRole(role)\n        return cls(role=role, blocks=[TextBlock(text=content)], **kwargs)\n\n    def _recursive_serialization(self, value: Any) -> Any:","sourceCodeStart":1215,"sourceCodeEnd":1251,"githubUrl":"https://github.com/run-llama/llama_index/blob/afd0fef371831f9bda13e5af7167cf4e981278ab/llama-index-core/llama_index/core/base/llms/types.py#L1215-L1251","documentation":"Raised by ChatMessage's backward-compat content setter when assigning content while blocks already contains multiple blocks, or a single non-TextBlock block. Setting .content is only allowed when blocks is empty or exactly one TextBlock; otherwise content would silently discard the other blocks.","triggerScenarios":"msg = ChatMessage(blocks=[TextBlock(...), ImageBlock(...)]) then msg.content = \"new text\"; or blocks=[AudioBlock(...)] then assigning content. Also ChatMessage(content=..., blocks=[multiple]) at construction via the content field validator.","commonSituations":"Older single-field code (msg.content = ...) run against the multi-block ChatMessage API after a library upgrade; mutating a multimodal message's text while forgetting it carries image/audio blocks.","solutions":["To update text on a multimodal message, replace the blocks list: msg.blocks = [TextBlock(text=\"new\"), *msg.blocks[1:]].","Construct with blocks only (no content kwarg) when using multiple or non-text blocks.","For text-only messages use ChatMessage.from_str(content, role) which cannot hit this path."],"exampleFix":"# before\nmsg = ChatMessage(blocks=[TextBlock(text=\"hi\"), ImageBlock(image=img_bytes)], role=MessageRole.USER)\nmsg.content = \"hi, what is this?\"  # ValueError\n\n# after\nmsg.blocks = [TextBlock(text=\"hi, what is this?\"), ImageBlock(image=img_bytes)]","handlingStrategy":"type-guard","validationCode":"def set_text(msg: ChatMessage, text: str) -> ChatMessage:\n    if len(msg.blocks) > 1 or not isinstance(msg.blocks[0] if msg.blocks else None, (type(None),)) and not (len(msg.blocks) <= 1):\n        msg.blocks = [TextBlock(text=text)] + [b for b in msg.blocks if not hasattr(b, \"text\")]\n    else:\n        msg.blocks = [TextBlock(text=text)]\n    return msg","typeGuard":"def can_set_content(msg: ChatMessage) -> bool:\n    return not msg.blocks or (len(msg.blocks) == 1 and hasattr(msg.blocks[0], \"text\"))","tryCatchPattern":null,"preventionTips":["After migrating to the blocks API, mutate msg.blocks instead of msg.content.","Use ChatMessage.from_str for pure-text messages."],"tags":["llama-index","chat-message","blocks","migration"],"backgroundTag":null,"analyzedSha":"afd0fef371831f9bda13e5af7167cf4e981278ab","analyzedAt":"2026-08-15T05:42:58.429Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}