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_use', but is missing expected key(s) {missing}. Full content block:
{block} What it means
Raised while converting an Anthropic-format AIMessage: a content block with `"type": "tool_use"` must carry `id`, `name`, and `input`, because the converter synthesizes an OpenAI `tool_calls` entry from them (`id` -> tool call id, `name`/`input` -> function name and JSON arguments). If any of the three keys is absent the mapping is impossible, so the conversion aborts with the missing key names listed in the message.
Source
Thrown at libs/core/langchain_core/messages/utils.py:1814
# OpenAI audio format
elif (
block.get("type") == "input_audio"
and isinstance(block.get("input_audio"), dict)
and isinstance(block.get("input_audio", {}).get("data"), str)
and isinstance(block.get("input_audio", {}).get("format"), str)
):
content.append(block)
elif block.get("type") == "tool_use":
if missing := [
k for k in ("id", "name", "input") if k not in block
]:
err = (
f"Unrecognized content block at "
f"messages[{i}].content[{j}] has 'type': "
f"'tool_use', but is missing expected key(s) "
f"{missing}. Full content block:\n\n{block}"
)
raise ValueError(err)
if not any(
tool_call["id"] == block["id"]
for tool_call in cast("AIMessage", message).tool_calls
):
oai_msg["tool_calls"] = oai_msg.get("tool_calls", [])
oai_msg["tool_calls"].append(
{
"type": "function",
"id": block["id"],
"function": {
"name": block["name"],
"arguments": json.dumps(
block["input"], ensure_ascii=False
),
},
}
)
elif block.get("type") == "function_call": # OpenAI ResponsesView on GitHub (pinned to e32fa9a52e)
Solutions
- Add an `id` (any unique string, Anthropic uses `toolu_...`) to every `tool_use` block.
- Prefer building AIMessage with `tool_calls=[...]` and let LangChain render content blocks, instead of hand-writing `tool_use` dicts.
- If the block is informational only, move the data into a `text` block instead of `tool_use`.
- Validate blocks with a small pre-check (see defense) before conversion and drop/repair incomplete ones.
Example fix
// before
{"type": "tool_use", "name": "search", "input": {"q": "cats"}}
// after
{"type": "tool_use", "id": "toolu_01XFDUDYJgAACzvnptvVoYEL", "name": "search", "input": {"q": "cats"}} Defensive patterns
Strategy: validation
Validate before calling
def valid_tool_use(b: dict) -> bool:
return b.get("type") == "tool_use" and all(k in b for k in ("id", "name", "input"))
for i, m in enumerate(messages):
for j, b in enumerate(m.content if isinstance(m.content, list) else []):
if b.get("type") == "tool_use" and not valid_tool_use(b):
raise ValueError(f"incomplete tool_use at messages[{i}].content[{j}]") Type guard
def is_complete_tool_use(b: dict) -> bool:
return (
b.get("type") == "tool_use"
and isinstance(b.get("id"), str)
and isinstance(b.get("name"), str)
and isinstance(b.get("input"), dict)
) Try / catch
try:
oai = convert_to_openai_messages(messages)
except ValueError as e:
if "tool_use" in str(e):
# repair: synthesize an id, or drop the block if it carries no real call
... Prevention
- Build assistant tool messages via AIMessage(tool_calls=[...]) instead of raw blocks
- Pair every tool_use with its tool_result by id in the same history
- Validate streamed tool calls only after the final delta, not mid-stream
When it happens
Trigger: Hand-authored assistant messages containing `{"type": "tool_use", "name": "search", "input": {...}}` without `id`; replaying captured Anthropic streams where the final `tool_use` delta was dropped; stripping `id` during message sanitization before `convert_to_openai_messages`.
Common situations: Building few-shot conversational histories for agents that use Anthropic content blocks; deserializing stored messages with lossy schemas; partial tool-call payloads from interrupted streaming.
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/80c9df3a9db0f1fc.
Report an issue: GitHub.