Interrupts
Interrupts enable human-in-the-loop workflows in LangGraph -- pausing execution, waiting for human input, and resuming with the provided value.
interrupt() Function
The interrupt() function pauses graph execution at the current point and waits for a resume value:
from langgraph.types import interrupt
def review_node(state):
# Pause and wait for human input
approved = interrupt(f"Review: {state['draft']}")
return {"approved": approved}
When hit, the graph sends an interrupt event. The workflow is checkpointed, safe for restart.
Command(resume=...)
Resume a paused graph by providing a value via Command:
from langgraph.types import Command
# Resume with True for approval:
graph.invoke(Command(resume=True), config)
# Resume with a string:
graph.invoke(Command(resume="Looks good, proceed"), config)
# Resume with structured data:
graph.invoke(Command(resume={"action": "edit", "edits": [...]}), config)
Patterns
Approval
def generate_email(state):
draft = llm.invoke(state["prompt"])
result = interrupt(f"Approve draft?\n\n{draft}")
return {"email": draft, "sent": bool(result)}
# User reviews, then:
graph.invoke(Command(resume=True), config)
Tool Review
def tool_executor(state):
tools = state["planned_tools"]
for tool_call in tools:
confirm = interrupt(f"Execute {tool_call['name']} with args {tool_call['args']}?")
if confirm:
result = execute(tool_call)
state["results"].append(result)
return {"results": state["results"]}
Editing State
def draft_blog(state):
draft = write_post(state["topic"])
edits = interrupt(f"Draft ready. Edit?\n{draft}")
if edits:
return {"draft": apply_edits(draft, edits)}
return {"draft": draft}
# Resume with edits:
graph.invoke(Command(resume="Fix typo in paragraph 2"), config)
Multiple Interrupts in Sequence
A single node can call interrupt() multiple times:
def multi_step(state):
step1 = interrupt("Step 1 input:")
state["step1"] = step1
step2 = interrupt("Step 2 input:")
state["step2"] = step2
return state
# Resume step by step:
graph.invoke(Command(resume="value_1"), config)
graph.invoke(Command(resume="value_2"), config)
Each invoke with Command resumes past the next interrupt() call.
Dynamic Interrupts
Use conditional logic to decide whether to interrupt:
def conditional_review(state):
if state.get("risk_score", 0) > 0.7:
action = interrupt(f"High risk ({state['risk_score']}). Override?")
if not action:
return {"abort": True}
return process(state)
Interrupts can be skipped entirely, triggered conditionally, or called with different prompts based on state.
Interrupts in Subgraphs
interrupt() works inside subgraphs. The interrupt propagates up to the parent:
# Subgraph:
subgraph_builder = StateGraph(SubState)
subgraph_builder.add_node("check", lambda s: {"result": interrupt("Confirm?")})
# Parent resumes the subgraph interrupt:
graph.invoke(Command(resume=True), config)
The parent manages the checkpoint and resume since the subgraph shares the parent's thread.
Combining Interrupts with Checkpointers
Interrupts rely on the checkpointer. The graph state is saved when interrupt() fires:
builder = StateGraph(State)
builder.add_node("review", review_node)
graph = builder.compile(checkpointer=SqliteSaver.from_conn_string("db.sqlite"))
config = {"configurable": {"thread_id": "workflow-1"}}
graph.invoke(inputs, config)
# Hits interrupt, checkpoint saved
# Resume from any client, any time:
graph.invoke(Command(resume=True), config)
Important: Code Before interrupt() Re-executes
On resume, execution restarts from the beginning of the node. Any code before the first interrupt() call re-executes:
def bad_pattern(state):
# This line RE-EXECUTES on resume!
state["audit_log"].append({"action": "review_started", "time": now()})
approved = interrupt("Approve?")
return {"approved": approved}
def good_pattern(state):
approved = interrupt("Approve?")
# This line only runs once (after resume):
state["audit_log"].append({"action": "review_completed", "time": now()})
return {"approved": approved}
Rule: Put
interrupt()as early as possible in the node. Keep pre-interrupt code minimal and idempotent.
Related: Persistence, Durable Execution, Memory