interrupt() works the first time but does not wait for input on the second run
Fixes LangGraph interrupt() silently not waiting for input on the second run. Use when the first interrupt works but the second invoke breezes past the interrupt node. Fixed in langgraph 0.2.63; older versions need the upgrade. Not for interrupts that never fire at all.
interrupt() works the first time but does not wait for user input on the second run
TL;DR: this is a LangGraph bug fixed in 0.2.63. Upgrade with pip install -U "langgraph>=0.2.63" and the second interrupt pauses correctly. No code change needed. If you are already past 0.2.63 and still see it, your resume call is probably not carrying the same thread_id.
# no error is raised. the symptom: the second graph.invoke() sails straight
# past interrupt() instead of pausing, so your human-in-the-loop step is skipped.When this applies
interrupt()inside a node or tool pauses correctly the FIRST time.- You resume with
Command(resume=...), the run finishes, and the NEXT invoke with a new input skips the interrupt entirely. - You are on langgraph below 0.2.63.
When it does not
- If interrupt never pauses even the first time, you are probably missing a checkpointer (interrupts need one to persist the pause).
- If resume throws, check that you pass the same
thread_idin the config.
Fix it
1. Upgrade langgraph
# terminal
pip install -U "langgraph>=0.2.63"Expected: the reported repro now pauses at interrupt() on every run, not just the first. The reporter confirmed the fix on 0.2.63 with no code changes.
2. Keep the thread_id stable across resume
config = {"configurable": {"thread_id": "user-1"}}
# first run: pauses at interrupt()
result = graph.invoke({"messages": [...]}, config)
# resume with the SAME config
result = graph.invoke(Command(resume="approved"), config)Expected: graph.get_state(config).next shows the interrupt node pending before resume, and empty after.
3. Verify the pause actually happened
snapshot = graph.get_state(config)
print(snapshot.next) # should list the node containing interrupt(), e.g. ('book_appointment',)
print(snapshot.tasks[0].interrupts) # the Interrupt objects waiting for a resume valueExpected: next is non-empty while paused. If it is empty, the graph did not pause and your resume value will be ignored.
Why it happens
Interrupts persist their paused state in the checkpointer keyed by thread. The pre-0.2.63 bug mishandled the checkpoint bookkeeping when a thread went through interrupt, resume, and then a second interrupt in a later run, so the second pause was never registered. The 0.2.63 release fixed the resume bookkeeping.
Edge cases
interrupt()inside a TOOL (withInjectedToolCallId) had the same bug; same fix.- After upgrading, old checkpoints written by the buggy version can still misbehave. Start a fresh thread_id to confirm the fix, then migrate.
- If you pin langgraph in a lockfile, bump the pin: the fix is not in 0.2.62 or earlier.
- JS users: the equivalent fix landed in the @langchain/langgraph 0.2.x line around the same time; check your changelog.
Compatibility
langgraph Python 0.2.63+ (fixed). Affects 0.2.x below 0.2.63. If you are on 1.x you already have the fix.