I’m building a LangGraph flow that calls interrupt() from inside a ToolNode, resumes the run while streaming (graph.stream(Command(resume=input_value))), then eventually finishes the tool and returns control to the node that originally invoked it which call a tool again (without finishing the thread) with interrupt() in it.
The firstinterrupt() works perfectly: the graph output stream includes the {"__interrupt__": …} envelope and I can resume as expected.
The second (and any subsequent) interrupt()does stop execution, but I get no "__interrupt__" indication in the streamed output. From the outside it just looks like the graph stalls—no error, no event, nothing to resume.
# simplified setup def tool_node(state): # first pause if state.get("stage") == 0: state["stage"] = 1 interrupt() # <- emits "__interrupt__" return state # do some work, then try pausing again
if state.get("stage") == 1: state["stage"] = 2 interrupt() # <- halts, but emits nothing return state # finish
graph = Graph() graph.add_node(tool_node)
# run runner = graph.stream({"stage": 0}) # => first interrupt event arrives; I call `graph.stream((Command(resume="foo"))` # ...second interrupt triggers but no "__interrupt__" appears
This occurs because LangGraph uses internal execution queues and the interrupt stream envelope is only surfaced once per event loop phase unless resumed. On LangGraph Platform, only the first interrupt emits an __interrupt__ event, subsequent interrupts don’t re-surface until you complete a resume cycle.
The resume cycle effect: After calling graph.stream(Command(resume="...")), execution continues and the next interrupt() can emit the event again.
Workaround: add state indicators:
def tool_node(state):
if state.get("stage") == 1:
state["interrupted_stage"] = "stage_2"
interrupt("Paused at stage 2")
return state
Detection strategies:
Monitor stream timing for stalled chunks
Check run status via API for interrupted state
Use state-based signaling before interrupt()
The graph remains alive awaiting resume, it doesn’t halt permanently. Test multi-interrupt flows on the platform since behavior differs from local execution.
It sounds like this is at least partially our problem, but some issues remains unclear.
how do you define a “completion of a resume cycle”?
This is our flow:
Agent ingest user message, call tool-1 which emits an interrupt, this is caught by our BE which triggers a re invocation with graph.stream(Command(resume="...")). It then returns to tool-1, finish it, returns to the agent which now calls tool-2. tool-2 tries to emit an interrupt, but this is not caught in the graph stream.
We assumed once we call graph.stream(Command(resume="..."))a new cycle begin, therefore enabling the graph to emit the new interrupt.
I don’t think that using the state indicator will work in this case, because tool-2 is being called in that same cycle and must emit an interrupt.
The only time this works for us, is when the stream ends and we start a new stream, enabling us to emit a new interrupt