ValueError: Found AIMessages with tool_calls that do not have a corresponding ToolMessage
Fixes the LangChain message-history error where an AI message contains tool calls but the matching ToolMessages are missing. Covers history trimming, filtering, and resume paths that break tool_call_id pairing.
TL;DR
Somewhere your message history lost the tool results while keeping the AI message that requested them. Providers reject that shape. The fix is to never split the pair: when you trim or filter history, drop the orphaned AI message too, or rebuild the list so every tool call has its ToolMessage.
ValueError: Found AIMessages with tool_calls that do not have a corresponding ToolMessageFix it
- Find where history gets modified: trimming, filtering, summarization, or a custom
get_session_history. Expected: you locate the spot that drops ToolMessages. - Keep pairs together. When you slice history, cut at message boundaries so an
AIMessagewithtool_callsis never separated from itsToolMessagereplies. Expected: re-running the same conversation no longer raises. - If you filter messages (for example removing old tool output to save tokens), also remove the AI message whose
tool_call_ids you deleted. Expected: the provider sees a clean alternating history. - In LangGraph, let
ToolNodeappend tool results instead of hand-rolling message surgery, and use theadd_messagesreducer so updates merge by id. Expected: pairing is maintained by the framework.
When this applies
- You trim, summarize, or filter chat history and then call the model again.
- You rebuild history on resume (from a checkpointer or your own store) and some ToolMessages are missing.
When this does NOT apply
- The error names a different missing piece (a ToolMessage without a matching AI call is the reverse problem).
- You never use tools; then the history shape issue is something else entirely.
Compatibility
- langchain-core 0.1+, LangGraph 0.1+. Applies to any provider; OpenAI and Anthropic both validate the pairing.
Root cause
Tool calling is a two-message contract: the AI message says "call this tool with this id" and a later ToolMessage says "here is the result for that id". Providers validate the contract server-side. Any history manipulation that deletes one side but keeps the other produces a conversation no provider will accept.
Edge cases
- Summarization that compresses tool exchanges into prose must remove both messages, not just the tool output.
- Parallel tool calls produce several ToolMessages for one AI message; keep all of them or drop the whole group.
- Streaming chunks reassembled by hand can lose tool_call ids; prefer the framework's chunk-merging helpers.
Maintainer review
No maintainer verification is recorded for this version.
This records the version a maintainer checked. It does not assert that the version is the latest upstream release.