Skip to content

fix(prebuilt): catch asyncio.CancelledError in ToolNode to prevent INVALID_CHAT_HISTORY#6945

Open
Subham Singhania (Subhamsinghania18) wants to merge 1 commit intolangchain-ai:mainfrom
Subhamsinghania18:fix/prebuilt-toolnode-cancelled-error
Open

fix(prebuilt): catch asyncio.CancelledError in ToolNode to prevent INVALID_CHAT_HISTORY#6945
Subham Singhania (Subhamsinghania18) wants to merge 1 commit intolangchain-ai:mainfrom
Subhamsinghania18:fix/prebuilt-toolnode-cancelled-error

Conversation

@Subhamsinghania18

asyncio.CancelledError was changed to inherit from BaseException (not
Exception) in Python 3.8 (bpo-32528). ToolNode's async error handlers
only caught Exception, so any task cancellation propagated unhandled,
leaving the message history in an invalid state — an AIMessage with
tool_calls but no corresponding ToolMessage. The next LLM call then
raised INVALID_CHAT_HISTORY.

Two sites are fixed:

  • _execute_tool_async — primary execution try/except block. A
    CancelledError handler is inserted between GraphBubbleUp (must always
    propagate) and the general Exception handler.
  • _arun_one wrapper block — the outer try/except wrapping
    _awrap_tool_call / _wrap_tool_call.

When handle_tool_errors is truthy a proper error ToolMessage is
returned to keep chat history valid. When falsy the error is re-raised
unchanged — zero behaviour change for the default case.

asyncio is already imported at the top of the module.

Issue: #6726
Dependencies: None

…on paths

asyncio.CancelledError was changed to inherit from BaseException
(not Exception) in Python 3.8 (bpo-32528).  ToolNode's async error
handlers only caught Exception, so a task cancellation propagated
unhandled, leaving the message history in an invalid state:

  AIMessage(tool_calls=[...])   <- no matching ToolMessage
  --> next LLM call raises INVALID_CHAT_HISTORY

Two sites are fixed:

1. _execute_tool_async — the primary execution try/except block.
   A CancelledError handler is inserted between GraphBubbleUp (must
   always propagate) and the general Exception handler.  When
   handle_tool_errors is truthy an error ToolMessage is returned to keep
   history valid; when it is falsy the error is re-raised unchanged.

2. _arun_one wrapper block — the outer try/except that wraps calls
   through _awrap_tool_call / _wrap_tool_call.  Same logic applies.

asyncio is already imported at the top of the module; no new imports
are required.

Closes: langchain-ai#6726
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

1 participant