langchain-ai/langchain · error · ValueError
Cannot concatenate ToolMessageChunks with different names.
Error message
Cannot concatenate ToolMessageChunks with different names.
What it means
Raised when two `ToolMessageChunk` objects are added together (`+`) but have different `tool_call_id` values. Chunk concatenation is how langchain-core aggregates streamed tool-message fragments into one message, and it is only valid between fragments of the same tool call. Note the message text says 'names' but the check is actually on `tool_call_id`.
Source
Thrown at libs/core/langchain_core/messages/tool.py:187
)
else:
super().__init__(content=content, **kwargs)
class ToolMessageChunk(ToolMessage, BaseMessageChunk):
"""Tool Message chunk."""
# Ignoring mypy re-assignment here since we're overriding the value
# to make sure that the chunk variant can be discriminated from the
# non-chunk variant.
type: Literal["ToolMessageChunk"] = "ToolMessageChunk" # type: ignore[assignment]
@override
def __add__(self, other: Any) -> BaseMessageChunk: # type: ignore[override]
if isinstance(other, ToolMessageChunk):
if self.tool_call_id != other.tool_call_id:
msg = "Cannot concatenate ToolMessageChunks with different names."
raise ValueError(msg)
return self.__class__(
tool_call_id=self.tool_call_id,
content=merge_content(self.content, other.content),
artifact=merge_obj(self.artifact, other.artifact),
additional_kwargs=merge_dicts(
self.additional_kwargs, other.additional_kwargs
),
response_metadata=merge_dicts(
self.response_metadata, other.response_metadata
),
id=self.id,
status=_merge_status(self.status, other.status),
)
return super().__add__(other)
View on GitHub (pinned to e32fa9a52e)
Solutions
- Group streamed chunks by `tool_call_id` before concatenating (dict keyed by tool_call_id) instead of summing everything into one accumulator
- Verify you are not merging chunks from two different tool calls returned in the same streaming response
- If writing a custom aggregator, check `chunk.tool_call_id == other.tool_call_id` before `+` and raise/store separately otherwise
- If you build `ToolMessageChunk` manually, pass the exact `tool_call_id` from the originating AI message's `tool_calls` entry
Example fix
# before (mixes parallel tool calls)
total = None
for chunk in stream:
total = chunk if total is None else total + chunk
# after
totals: dict[str, ToolMessageChunk] = {}
for chunk in stream:
prev = totals.get(chunk.tool_call_id)
totals[chunk.tool_call_id] = chunk if prev is None else prev + chunk Defensive patterns
Strategy: validation
Validate before calling
def can_concat(a: ToolMessageChunk, b: ToolMessageChunk) -> bool:
return a.tool_call_id == b.tool_call_id
# before accumulating streamed chunks:
if total is not None and not can_concat(total, chunk):
raise ValueError(f'chunk belongs to tool_call_id={chunk.tool_call_id}, buffer holds {total.tool_call_id}')
total = total + chunk if total is not None else chunk Type guard
from langchain_core.messages import ToolMessageChunk
def same_tool_call(a: ToolMessageChunk, b: ToolMessageChunk) -> bool:
return isinstance(a, ToolMessageChunk) and isinstance(b, ToolMessageChunk) and a.tool_call_id == b.tool_call_id Try / catch
try:
merged = a + b
except ValueError as e:
if 'different names' in str(e):
# keep chunks separate per tool_call_id instead of crashing
buffers[a.tool_call_id] = a
buffers[b.tool_call_id] = b
else:
raise Prevention
- Key streaming accumulation buffers by tool_call_id rather than using one running total
- Never assume parallel tool calls share a tool_call_id
- Log tool_call_id when streaming starts so mismatches are diagnosable
When it happens
Trigger: Calling `chunk1 + chunk2` (or `sum(chunks)`, or letting a streaming aggregator merge chunks) where the two `ToolMessageChunk` instances were created with different `tool_call_id` values, e.g. mixing fragments of two parallel tool calls into one accumulation buffer.
Common situations: Streaming agents that issue multiple tool calls per turn and accumulate all incoming chunks into a single variable without grouping by `tool_call_id`; manually re-indexing chunks after an id remapping step; writing a custom streaming callback that concatenates chunks out of order.
Related errors
- SyncTextProjection requires a string delta
- SyncTextProjection requires a string final value
- SyncTextProjection received a non-string delta
- SyncTextProjection received a non-string final value
- AsyncTextProjection received a non-string delta
AI-assisted analysis of langchain-ai/langchain@e32fa9a52e (2026-08-14).
Data as JSON: /api/errors/d6feb50d34f73219.
Report an issue: GitHub.