{"record":{"id":"a73938c663d167ec","repo":"langchain-ai/langchain","slug":"cannot-concatenate-chatmessagechunks-with-differen","errorCode":null,"errorMessage":"Cannot concatenate ChatMessageChunks with different roles.","messagePattern":"Cannot concatenate ChatMessageChunks with different roles\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"libs/core/langchain_core/messages/chat.py","lineNumber":39,"sourceCode":"    type: Literal[\"chat\"] = \"chat\"\n    \"\"\"The type of the message (used during serialization).\"\"\"\n\n\nclass ChatMessageChunk(ChatMessage, BaseMessageChunk):\n    \"\"\"Chat Message chunk.\"\"\"\n\n    # Ignoring mypy re-assignment here since we're overriding the value\n    # to make sure that the chunk variant can be discriminated from the\n    # non-chunk variant.\n    type: Literal[\"ChatMessageChunk\"] = \"ChatMessageChunk\"  # type: ignore[assignment]\n    \"\"\"The type of the message (used during serialization).\"\"\"\n\n    @override\n    def __add__(self, other: Any) -> BaseMessageChunk:  # type: ignore[override]\n        if isinstance(other, ChatMessageChunk):\n            if self.role != other.role:\n                msg = \"Cannot concatenate ChatMessageChunks with different roles.\"\n                raise ValueError(msg)\n\n            return self.__class__(\n                role=self.role,\n                content=merge_content(self.content, other.content),\n                additional_kwargs=merge_dicts(\n                    self.additional_kwargs, other.additional_kwargs\n                ),\n                response_metadata=merge_dicts(\n                    self.response_metadata, other.response_metadata\n                ),\n                id=self.id,\n            )\n        if isinstance(other, BaseMessageChunk):\n            return self.__class__(\n                role=self.role,\n                content=merge_content(self.content, other.content),\n                additional_kwargs=merge_dicts(\n                    self.additional_kwargs, other.additional_kwargs","sourceCodeStart":21,"sourceCodeEnd":57,"githubUrl":"https://github.com/langchain-ai/langchain/blob/e32fa9a52eab3b61ad7a45399bfde59b3e580fc4/libs/core/langchain_core/messages/chat.py#L21-L57","documentation":"Raised by ChatMessageChunk.__add__ when two chunks with different `role` values are concatenated with `+`. Chunk merging is only defined for pieces of the same logical message; a role change mid-stream would silently corrupt the message, so langchain-core refuses.","triggerScenarios":"`chunk_a + chunk_b` where `chunk_a.role == \"assistant\"` and `chunk_b.role == \"user\"` (or any differing roles); commonly happens when concatenating chunks from different messages in a stream, or merging an accumulated chunk with a stray chunk from a new message.","commonSituations":"Custom streaming code that reduces all incoming chunks with `+` without resetting the accumulator when the role/message changes; mixing chunks from parallel streams; unit tests that merge arbitrary chunks.","solutions":["Reset your accumulator when a new message (or role) starts instead of continuing to `+`","Guard before merging: only add when `left.role == right.role`","If roles genuinely differ, keep them as separate messages rather than merging"],"exampleFix":"# before\nacc = None\nfor chunk in stream:\n    acc = chunk if acc is None else acc + chunk  # ValueError across roles\n\n# after\nacc = None\nfor chunk in stream:\n    if acc is None or acc.role == chunk.role:\n        acc = chunk if acc is None else acc + chunk\n    else:\n        finalize(acc); acc = chunk","handlingStrategy":"type-guard","validationCode":"def same_role(a, b) -> bool:\n    return getattr(a, \"role\", None) == getattr(b, \"role\", None)","typeGuard":"from langchain_core.messages import ChatMessageChunk\n\ndef is_same_role_chunk(a: ChatMessageChunk, b: ChatMessageChunk) -> bool:\n    return a.role == b.role","tryCatchPattern":null,"preventionTips":["Reset chunk accumulators when the role changes between streamed chunks","Group chunks by (role, message id) before merging","Unit test streaming merges against multi-role streams"],"tags":["messages","streaming","chunks","chat-message","concatenation"],"backgroundTag":null,"analyzedSha":"e32fa9a52eab3b61ad7a45399bfde59b3e580fc4","analyzedAt":"2026-08-14T18:42:09.092Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}