langchain-ai/langchain · error · ValueError
Unrecognized content block at messages[{i}].content[{j}] has
Error message
Unrecognized content block at messages[{i}].content[{j}] has 'type': 'tool_result', but is missing expected key(s) {missing}. Full content block:
{block} What it means
While converting an Anthropic-style tool result, `convert_to_openai_messages` found a block with `"type": "tool_result"` lacking `content` or `tool_use_id`. Both are mandatory: `tool_use_id` becomes the ToolMessage's `tool_call_id` (OpenAI requires it to match the originating call) and `content` becomes the tool output. Missing either makes the resulting OpenAI `tool` message invalid, so conversion fails.
Source
Thrown at libs/core/langchain_core/messages/utils.py:1872
"function": {
"name": block.get("name"),
"arguments": block.get("arguments"),
},
}
)
if pass_through_unknown_blocks:
content.append(block)
elif block.get("type") == "tool_result":
if missing := [
k for k in ("content", "tool_use_id") if k not in block
]:
msg = (
f"Unrecognized content block at "
f"messages[{i}].content[{j}] has 'type': "
f"'tool_result', but is missing expected key(s) "
f"{missing}. Full content block:\n\n{block}"
)
raise ValueError(msg)
tool_message = ToolMessage(
block["content"],
tool_call_id=block["tool_use_id"],
status="error" if block.get("is_error") else "success",
)
# Recurse to make sure tool message contents are OpenAI format.
tool_messages.extend(
convert_to_openai_messages(
[tool_message], text_format=text_format
)
)
elif (block.get("type") == "json") or "json" in block:
if "json" not in block:
msg = (
f"Unrecognized content block at "
f"messages[{i}].content[{j}] has 'type': 'json' "
f"but does not have a 'json' key. Full "
f"content block:\n\n{block}"View on GitHub (pinned to e32fa9a52e)
Solutions
- Always include both keys: `{"type": "tool_result", "tool_use_id": "toolu_1", "content": "result text"}`.
- If content is empty, use an empty string/list rather than omitting the key.
- Prefer LangChain's ToolMessage (`ToolMessage(content, tool_call_id=...)`) which the converter handles natively.
- Cross-check that every `tool_result.tool_use_id` matches an earlier `tool_use.id` in the same history.
Example fix
// before
{"type": "tool_result", "content": "Paris: 22C"}
// after
{"type": "tool_result", "tool_use_id": "toolu_01ABC", "content": "Paris: 22C"} Defensive patterns
Strategy: validation
Validate before calling
def valid_tool_result(b: dict) -> bool:
return b.get("type") == "tool_result" and "content" in b and "tool_use_id" in b
for b in blocks:
if b.get("type") == "tool_result" and not valid_tool_result(b):
b.setdefault("content", "")
# tool_use_id cannot be invented; raise with a clear message instead
if "tool_use_id" not in b:
raise ValueError(f"tool_result missing tool_use_id: {b}") Type guard
def is_complete_tool_result(b: dict) -> bool:
return (
b.get("type") == "tool_result"
and isinstance(b.get("tool_use_id"), str)
and (isinstance(b.get("content"), (str, list)) or "content" in b)
) Try / catch
try:
oai = convert_to_openai_messages(history)
except ValueError as e:
if "tool_result" in str(e):
# locate the message pair by id in the error text and re-emit a complete ToolMessage
... Prevention
- Use ToolMessage(content=..., tool_call_id=...) as the source of truth for results
- Keep empty string content rather than deleting the key
- Round-trip test full tool-call/tool-result cycles through your persistence layer
When it happens
Trigger: `{"type": "tool_result", "content": "42"}` with no `tool_use_id`, or `{"type": "tool_result", "tool_use_id": "toolu_1"}` with no `content` key; also `is_error: true` blocks truncated during capture.
Common situations: Manually appending tool outputs to conversation history for Anthropic-style agents; replaying logged Anthropic conversations where `content` was serialized as `None` and dropped; mismatched sanitizers that delete empty strings.
Related errors
- Unrecognized content block at messages[{i}].content[{j}] has
- Unrecognized content block at messages[{i}].content[{j}] has
- Unrecognized content block at messages[{i}].content[{j}] has
- Unrecognized content block at messages[{i}].content[{j}] doe
- Unrecognized content block at messages[{i}].content[{j}] has
AI-assisted analysis of langchain-ai/langchain@e32fa9a52e (2026-08-14).
Data as JSON: /api/errors/10882ddab5d8841e.
Report an issue: GitHub.