langchain - 💡(How to fix) Fix BUG: tool_search_tool_result blocks retain stream-chunk index field, breaking multi-turn API calls

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Error Message

anthropic.BadRequestError: Error code: 400 - {
  "type": "error",
  "error": {
    "type": "invalid_request_error",
    "message": "messages.N.content.M.tool_search_tool_result.index: Extra inputs are not permitted"
  }
}

Fix Action

Workaround

Avoid binding tool_search_tool_regex_20251119 on agents with small tool surfaces where lazy loading is not needed. This prevents Anthropic from returning tool_search_tool_result blocks entirely.

Code Example

from langchain_anthropic import ChatAnthropic
from langchain_core.messages import HumanMessage, ToolMessage
from langchain.tools import tool

model = ChatAnthropic(
    model="claude-opus-4-5-20251101",   # or any claude-3.7+
    betas=["advanced-tool-use-2025-11-20"],
)

@tool
def my_tool(query: str) -> str:
    """A regular tool."""
    return f"result for {query}"

# Bind with tool_search builtin (triggers tool_search_tool_result blocks)
model_with_tools = model.bind_tools(
    [my_tool],
    tool_choice="auto",
)
# Internally, ChatAnthropic adds tool_search_tool_regex_20251119 builtin
# when deferred-loading tools are present. The key is streaming.

messages = [HumanMessage(content="Use my_tool to look up 'hello'")]

# Turn 1: stream response (streaming causes index fields to appear in content blocks)
ai_msg = model_with_tools.invoke(messages)
# ai_msg.content will contain dicts with an "index" key on tool_search_tool_result blocks

# Inspect the assembled content
print([block for block in ai_msg.content if isinstance(block, dict) and block.get("type") == "tool_search_tool_result"])
# Output: [{'type': 'tool_search_tool_result', 'tool_use_id': '...', 'content': [...], 'index': 2}]
#                                                                                               ^^^^^^^^ should not be here

# Turn 2: append AI message + tool result and call again
tool_result = ToolMessage(content="result for hello", tool_call_id=ai_msg.tool_calls[0]["id"])
messages = messages + [ai_msg, tool_result]

model_with_tools.invoke(messages)
# ^^^ Raises HTTP 400:
# "messages.N.content.M.tool_search_tool_result.index: Extra inputs are not permitted"

---

anthropic.BadRequestError: Error code: 400 - {
  "type": "error",
  "error": {
    "type": "invalid_request_error",
    "message": "messages.N.content.M.tool_search_tool_result.index: Extra inputs are not permitted"
  }
}
RAW_BUFFERClick to expand / collapse

Checklist

  • This is a bug, not a usage question.
  • I added a clear and descriptive title that summarizes this issue.
  • I used the GitHub search to find a similar question and didn't find it.
  • I am sure that this is a bug in LangChain rather than my code.
  • The bug is not resolved by updating to the latest stable version of LangChain (or the specific integration package).

Package

  • langchain-anthropic

Related issue

Tangentially related to #34767 (streaming parse failure during tool_search), but this is a distinct downstream problem: the index field from streaming chunk assembly is not stripped before the assembled message enters conversation history, causing the next API call to 400.

Reproduction Steps

from langchain_anthropic import ChatAnthropic
from langchain_core.messages import HumanMessage, ToolMessage
from langchain.tools import tool

model = ChatAnthropic(
    model="claude-opus-4-5-20251101",   # or any claude-3.7+
    betas=["advanced-tool-use-2025-11-20"],
)

@tool
def my_tool(query: str) -> str:
    """A regular tool."""
    return f"result for {query}"

# Bind with tool_search builtin (triggers tool_search_tool_result blocks)
model_with_tools = model.bind_tools(
    [my_tool],
    tool_choice="auto",
)
# Internally, ChatAnthropic adds tool_search_tool_regex_20251119 builtin
# when deferred-loading tools are present. The key is streaming.

messages = [HumanMessage(content="Use my_tool to look up 'hello'")]

# Turn 1: stream response (streaming causes index fields to appear in content blocks)
ai_msg = model_with_tools.invoke(messages)
# ai_msg.content will contain dicts with an "index" key on tool_search_tool_result blocks

# Inspect the assembled content
print([block for block in ai_msg.content if isinstance(block, dict) and block.get("type") == "tool_search_tool_result"])
# Output: [{'type': 'tool_search_tool_result', 'tool_use_id': '...', 'content': [...], 'index': 2}]
#                                                                                               ^^^^^^^^ should not be here

# Turn 2: append AI message + tool result and call again
tool_result = ToolMessage(content="result for hello", tool_call_id=ai_msg.tool_calls[0]["id"])
messages = messages + [ai_msg, tool_result]

model_with_tools.invoke(messages)
# ^^^ Raises HTTP 400:
# "messages.N.content.M.tool_search_tool_result.index: Extra inputs are not permitted"

Error

anthropic.BadRequestError: Error code: 400 - {
  "type": "error",
  "error": {
    "type": "invalid_request_error",
    "message": "messages.N.content.M.tool_search_tool_result.index: Extra inputs are not permitted"
  }
}

Expected behaviour

The index field on tool_search_tool_result content blocks should be stripped before (or when) the assembled AIMessage is stored in conversation history. index is a streaming-transport artifact used for client-side chunk assembly; it is not part of the send-shape defined by Anthropic's API.

Anthropic SDK ToolSearchToolResultBlockParam (anthropic 0.101.0, anthropic/types/tool_search_tool_result_block_param.py) explicitly defines the send shape as {content, tool_use_id, type, cache_control} — no index field.

Actual behaviour

langchain_anthropic retains the raw streaming chunk fields verbatim in the assembled AIMessage.content list. On the next API call, those dicts (including index) are serialized and sent back to Anthropic, which rejects them.

Note: the same class of bug exists for tool_use blocks (see #31208, now closed), but it has resurfaced specifically for tool_search_tool_result blocks which were added later.

Versions

  • langchain-anthropic: 1.4.3
  • anthropic: 0.101.0
  • langchain-core: 1.3.x
  • Python: 3.12

Workaround

Avoid binding tool_search_tool_regex_20251119 on agents with small tool surfaces where lazy loading is not needed. This prevents Anthropic from returning tool_search_tool_result blocks entirely.

Suggested fix location

langchain_anthropic/chat_models.py — wherever streaming chunks are assembled into the final AIMessage.content list. Strip index (and any other transport-only fields) from content block dicts before appending to the message that will be re-sent as history.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING