How to cleanly stop the workflow of the react agent from the tool?

In my react_agent, I’ve implemented an interrupt() within one of the tools. When the user inputs “cancel”, I want the flow to stop entirely and display a message like “Cancelled by user” without continuing or restarting the agent flow.

Initially, I tried returning the string 'cancel' from the tool, expecting the agent to interpret this and terminate the flow. While this sometimes worked, other times it would restart the agent unexpectedly.

To improve this, I then tried raising an OutputParserException like so:

python

raise OutputParserException("Cancelled by user. No changes were made.")

This approach worked more reliably, but recently, the agent has started re-triggering the flow again even after this exception, resulting in an infinite loop.

How can I cleanly stop the flow from a tool when the user cancels, without restarting or looping the agent?

To cleanly stop the flow from a tool without restarting, use LangGraph’s Command to explicitly end execution. In your tool, after the interrupt and user cancellation, return a Command that goes to a dedicated termination node:

from langgraph.constants import END
from langgraph.types import Command

# In your tool
if user_input == "cancel":
    return Command(goto="cancelled")

# Add a cancellation node to your graph
def cancelled_node(state):
    return {"messages": [AIMessage("Cancelled by user. No changes were made.")]}

# In your graph definition
.addNode("cancelled", cancelled_node)
.addEdge("cancelled", END)

Alternatively, you can go directly to END:

if user_input == "cancel":
    return Command(goto=END)

This bypasses the agent’s retry logic and cleanly terminates the workflow. Avoid exceptions as they trigger the agent’s error handling, which can cause restarts.

I’m using Command() in my tool to pause execution and ask the user for confirmation before proceeding. Here’s the code I used:

@tool
def execute_orm(
orm_query_generated: str,
tool_call_id: Annotated[str, InjectedToolCallId],
state: Annotated[BasicChatState, InjectedState]
):
“”“Validates and executes the ORM query”“”

response = interrupt("Do you want to proceed?")

if response == 'proceed':
    obj = eval(orm_query_generated)
    return obj

elif response == 'cancel':
    state['messages'].append(
        ToolMessage(tool_call_id=tool_call_id, content="Operation cancelled by user.")
    )
    return Command(goto=END)

However, I’m getting the following error:

ValueError: Found AIMessages with tool_calls that do not have a corresponding ToolMessage.

This error is raised from langgraph/prebuilt/chat_agent_executor.py.

Any idea why this is happening, and how to properly use Command() in this case?

@eric-langchain
@AbdulBasit
@eyurtsev
can u pls help me out ?

To update the value of the state (e.g., the messages), the code should return a request to update the state. Specifically, the code should not be attempting to mutate or set the value of the state in place. state[‘messages’] = … or state[‘messages’].append(…) will not work.

Instead the options are:

  1. Request a state update when you return information from a node (e.g., you return a dictionary with keys and the new values for the keys)
  2. Request a state update by returning a Command primitive from a tool. The command primitive has a field that allows you to request an update.

You can find examples that use Command in the langgraph docs.

1 Like