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

  1. Always include both keys: `{"type": "tool_result", "tool_use_id": "toolu_1", "content": "result text"}`.
  2. If content is empty, use an empty string/list rather than omitting the key.
  3. Prefer LangChain's ToolMessage (`ToolMessage(content, tool_call_id=...)`) which the converter handles natively.
  4. 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

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


AI-assisted analysis of langchain-ai/langchain@e32fa9a52e (2026-08-14). Data as JSON: /api/errors/10882ddab5d8841e. Report an issue: GitHub.