Skip to main content
Some tool operations may be sensitive and require human approval before execution. Deep Agents support human-in-the-loop workflows through LangGraph’s interrupt capabilities. You can configure which tools require approval using the interrupt_on parameter. When interrupt_on is set, HumanInTheLoopMiddleware is added to the Deep Agents stack. If a run is cancelled or interrupted before a tool returns a result, PatchToolCallsMiddleware in the same stack repairs the message history automatically.

Basic configuration

The interrupt_on parameter accepts a dictionary mapping tool names to interrupt configurations. Each tool can be configured with:
  • True: Enable interrupts with default behavior (approve, edit, reject, and respond allowed)
  • False: Disable interrupts for this tool
  • InterruptOnConfig: Custom configuration. Set allowed_decisions to control review options. In Python, add an optional when predicate to interrupt only specific calls (see Conditional interrupts).

Decision types

The allowed_decisions list controls what actions a human can take when reviewing a tool call: Use reject when the human denies a proposed action. Use respond only when the human is acting as the tool, such as answering an ask_user prompt. Do not use respond to deny side-effecting tools, because its message may be treated by the model as a successful tool result.
When editing tool arguments, make changes conservatively. Significant modifications to the original arguments may cause the model to re-evaluate its approach and potentially execute the tool multiple times or take unexpected actions.
You can customize which decisions are available for each tool:

Conditional interrupts

By default, every tool call listed in interrupt_on pauses for review. To pause only some calls, add a when predicate to a tool’s InterruptOnConfig. The predicate receives a ToolCallRequest and returns True to interrupt or False to auto-approve, so you can gate on the tool’s arguments.
Conditional interrupts require langchain>=1.3.3.
When the when predicate returns False, the call runs without interrupting. When it returns True, or when you omit when, the call pauses as usual. Calls that evaluate to False are never added to the interrupt batch, so a reviewer only sees the actions that need a decision. See the LangChain human-in-the-loop documentation for additional configuration options and examples.

Handle interrupts

When an interrupt is triggered, the agent pauses execution and returns control. Check for interrupts in the result and handle them accordingly. If the user rejects an action, include a clear message that tells the agent the tool was not executed and what to do next.

Handle interrupts with streaming

For real-time UIs, use .stream() instead of .invoke() to display tokens and tool call progress while handling HITL interrupts. Interrupts appear as __interrupt__ entries in the updates stream. After the stream completes, collect human decisions and resume by streaming again with Command(resume=...).
For a typical interrupt_on HITL pause, resume with a flat {"decisions": [...]} payload, the same shape as with .invoke(). When multiple interrupts are pending at once (for example, parallel branches), map each interrupt ID to its resume value instead. See Handling multiple interrupts.
Combine "messages" and "updates" stream modes to show real-time LLM tokens to the user while still catching HITL interrupts. The "messages" mode streams tokens; the "updates" mode delivers interrupt payloads.

Multiple tool calls

When the agent calls multiple tools that require approval, all interrupts are batched together in a single interrupt. You must provide decisions for each one in order.

Rejection messages

When a reviewer returns a reject decision, Deep Agents skip the tool call and send rejection feedback back to the agent. If you omit message, the default feedback tells the model that the tool was not executed and not to retry the same tool call unless the user asks. For sensitive or side-effecting tools, pass a domain-specific message with the decision. Be explicit about whether the agent should abandon the action, ask a follow-up question, or try a safer alternative.

Edit tool arguments

When "edit" is in the allowed decisions, you can modify the tool arguments before execution:

Subagent interrupts

When using subagents, you can use interrupts on tool calls and within tool calls.

Interrupts on tool calls

Each subagent can have its own interrupt_on configuration that overrides the main agent’s settings:
When a subagent triggers an interrupt, the handling is the same—check for __interrupt__ on the result and resume with Command. When streaming, subagent interrupts also surface as __interrupt__ entries in the parent updates stream. You do not need subgraphs=True for interrupt delivery. Enable subgraphs=True if you also want to stream subagent tokens and progress events. See Handle interrupts with streaming for the full pattern.

Interrupts within tool calls

Subagent tools can call interrupt() directly to pause execution and await approval:
When run, this produces the following output:

Filesystem permission interrupts

Filesystem permission interrupts require deepagents>=0.6.8.
Beyond interrupt_on, you can pause the built-in filesystem tools by marking a permission rule with mode="interrupt". When the agent calls write_file or edit_file on a path that matches an interrupt-mode rule, create_deep_agent raises the same human-in-the-loop interrupt as a configured tool, using the filesystem tool’s name as the action name.
Handle and resume the interrupt the same way as a tool-call interrupt: run until it pauses, inspect the request, then resume with a decision.
Filesystem-permission interrupts merge with any interrupt_on you pass, so a single review step can cover both custom tools and protected filesystem paths.

Best practices

Always use a checkpointer

Human-in-the-loop requires a checkpointer to persist agent state between the interrupt and resume:

Use the same thread ID

When resuming, you must use the same config with the same thread_id:

Match decision order to actions

The decisions list must match the order of action_requests:

Tailor configurations by risk

Configure different tools based on their risk level: