# LangGraph 1.2.15: Multiple interrupts in one node

> One node calls interrupt() three times and is answered one resume at a time (matched by position), plus two parallel nodes that interrupt in the same step and are resumed together with a dict keyed by Interrupt.id.

Status: tested 2026-10-15 · Python 3.13 (tested) · License: MIT · Tier: Free
Download: https://tryagentsignal.com/downloads/langgraph-multi-interrupt-starter.zip

## What it shows
- **Part 1: sequential interrupts**: Each invoke(Command(resume=v)) answers the next pending question. The node re-executes from the top each time (4 executions for 3 interrupts); earlier answers are replayed by position.
- **Part 2: parallel interrupts**: Two parallel nodes interrupt in the same step. out["__interrupt__"] lists both with ids, and one Command(resume={interrupt.id: value, ...}) answers both, matched by id.

## Mocked vs real
No LLM and nothing is mocked. All LangGraph code is real (langgraph==1.2.15, langgraph-checkpoint==4.3.0, InMemorySaver).

## Gotchas
- Within one node, resume values match interrupt() calls by order. Keep the sequence deterministic: no interrupts inside conditionals that can change between executions, no loops over unordered or random data.
- Code between interrupts runs again on every resume, so keep side effects out of it or make them idempotent (see the idempotent approval template).
- When several interrupts are pending at once, resume with a dict keyed by Interrupt.id; a bare value would be ambiguous.
- To ask several things in one go, a single interrupt() carrying a list keyed by your own ids is often simpler.

## FAQ
### How are multiple resume values matched in one node?
By order of the interrupt() calls within the node, which is why the call sequence must be deterministic.

### How do I resume two parallel interrupts at once?
Pass Command(resume={interrupt_id: value, ...}) using the ids listed in out["__interrupt__"].

### Which versions was it tested on?
langgraph 1.2.15 and langgraph-checkpoint 4.3.0 on Python 3.13, Oct 15, 2026. MIT-licensed.

## Code

### multi_interrupt.py

```python
"""Multiple interrupts in ONE node, plus parallel interrupts resumed by interrupt id. No LLM; all LangGraph real."""
import operator
from typing import TypedDict, Annotated
from langgraph.graph import StateGraph, START, END
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.types import interrupt, Command

class S(TypedDict, total=False):
    answers: Annotated[list, operator.add]

calls = {"n": 0}
def review(s):
    calls["n"] += 1
    print(f"  [review] execution #{calls['n']}")
    a = interrupt({"step": 1, "q": "Approve deleting 1200 rows?"})
    b = interrupt({"step": 2, "q": "Also notify the customer?"})
    c = interrupt({"step": 3, "q": "Reason for audit log?"})
    return {"answers": [a, b, c]}

def part1():
    print("== Part 1: three sequential interrupt() calls in one node ==")
    b = StateGraph(S); b.add_node("review", review); b.add_edge(START, "review"); b.add_edge("review", END)
    g = b.compile(checkpointer=InMemorySaver()); cfg = {"configurable": {"thread_id": "seq"}}
    out = g.invoke({"answers": []}, cfg)
    for resume in [True, False, "cleanup ticket #881"]:
        print("  paused on:", out["__interrupt__"][0].value, "-> resume", repr(resume))
        out = g.invoke(Command(resume=resume), cfg)
    print("  final:", out)

def part2():
    print("== Part 2: two parallel nodes each interrupt; resume both at once keyed by interrupt id ==")
    def mk(name):
        def node(s):
            return {"answers": [(name, interrupt({"node": name, "q": f"approve {name}?"}))]}
        return node
    b = StateGraph(S)
    for n in ("tool_a", "tool_b"):
        b.add_node(n, mk(n)); b.add_edge(START, n); b.add_edge(n, END)
    g = b.compile(checkpointer=InMemorySaver()); cfg = {"configurable": {"thread_id": "par"}}
    out = g.invoke({"answers": []}, cfg)
    intrs = out["__interrupt__"]
    print("  pending:", [(i.id[:8], i.value["node"]) for i in intrs])
    decisions = {i.id: (i.value["node"] == "tool_a") for i in intrs}  # approve a, reject b
    out = g.invoke(Command(resume=decisions), cfg)
    print("  final:", sorted(out["answers"]))

part1(); calls["n"] = 0; part2()

```

### requirements.txt

```
langgraph==1.2.15
langgraph-checkpoint==4.3.0

```

### run_output.txt

```
== Part 1: three sequential interrupt() calls in one node ==
  [review] execution #1
  paused on: {'step': 1, 'q': 'Approve deleting 1200 rows?'} -> resume True
  [review] execution #2
  paused on: {'step': 2, 'q': 'Also notify the customer?'} -> resume False
  [review] execution #3
  paused on: {'step': 3, 'q': 'Reason for audit log?'} -> resume 'cleanup ticket #881'
  [review] execution #4
  final: {'answers': [True, False, 'cleanup ticket #881']}
== Part 2: two parallel nodes each interrupt; resume both at once keyed by interrupt id ==
  pending: [('0965f4b8', 'tool_a'), ('1c911b00', 'tool_b')]
  final: [('tool_a', True), ('tool_b', False)]

```
