-
Notifications
You must be signed in to change notification settings - Fork 4.3k
Open
Description
Description
When a tool execution is cancelled via asyncio.CancelledError, the ToolNode does not create an error ToolMessage even when handle_tool_errors=True. This leaves the message history in an invalid state where an AIMessage has tool_calls without corresponding ToolMessages.
Root Cause
asyncio.CancelledError inherits from BaseException, not Exception. The error handling in ToolNode._arun_one() uses except Exception as e:, which doesn't catch CancelledError:
# langgraph/prebuilt/tool_node.py line ~951
except GraphBubbleUp:
raise
except Exception as e: # CancelledError bypasses this!
# ... handle_tool_errors logic ...Reproduction
import asyncio
from langchain_core.messages import AIMessage, HumanMessage, ToolMessage
from langchain_core.tools import tool
from langgraph.prebuilt import ToolNode
@tool
async def slow_tool(query: str) -> str:
"""A tool that takes time to complete."""
await asyncio.sleep(10)
return f"Result: {query}"
async def test_cancelled_error():
tool_node = ToolNode(tools=[slow_tool], handle_tool_errors=True)
ai_message = AIMessage(
content="",
tool_calls=[{"id": "call_1", "name": "slow_tool", "args": {"query": "test"}}]
)
state = {"messages": [HumanMessage(content="test"), ai_message]}
config = {"configurable": {"thread_id": "test"}}
task = asyncio.create_task(tool_node.ainvoke(state, config))
await asyncio.sleep(0.1)
task.cancel()
try:
await task
except asyncio.CancelledError:
print("CancelledError raised - no ToolMessage created!")
# State now has AIMessage with tool_calls but no ToolMessage
# This causes INVALID_CHAT_HISTORY on next LLM call
asyncio.run(test_cancelled_error())Output:
CancelledError raised - no ToolMessage created!
Expected Behavior
When handle_tool_errors=True and a tool is cancelled, ToolNode should catch CancelledError and return a ToolMessage with an error status, maintaining valid chat history.
Suggested Fix
Either:
- Catch
BaseExceptionand re-raise after creating errorToolMessageforCancelledError - Explicitly catch
asyncio.CancelledErrorbefore theexcept Exceptionblock
except asyncio.CancelledError:
if self._handle_tool_errors:
return ToolMessage(
content="Tool execution was cancelled",
name=call["name"],
tool_call_id=call["id"],
status="error",
)
raise
except Exception as e:
# existing handling...Environment
- langgraph version: 1.0.7 (also confirmed in 0.5.4)
- Python version: 3.11
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels