langgraph interrupt "__interrupt__ called without a checkpointer"
Fixes the LangGraph error raised when interrupt() is used on a graph compiled without a checkpointer. Use when adding human-in-the-loop pauses to a graph that was compiled bare, or when the error appears after refactoring compile args. Not for resume-from-interrupt bugs, which need the same thread_id instead.
TL;DR
interrupt() only works when the graph was compiled with a checkpointer, because the pending state has to be persisted somewhere. Fix it by compiling with a checkpointer such as MemorySaver and always passing a thread_id in the config.
__interrupt__ called without a checkpointerWhen to use
- You added
interrupt()to a graph node and hit this error on invoke - A graph that ran fine breaks after you add human-in-the-loop pauses
- The error appears right after refactoring how the graph is compiled
When not to use
- The interrupt fires but resuming finds no state (thats a thread_id mismatch)
- You want interrupts in a stateless one-shot run (interrupts fundamentally need persisted state)
- The checkpointer itself raises connection errors (thats the store)
Steps
- Compile the graph with a checkpointer:
from langgraph.checkpoint.memory import MemorySaver
checkpointer = MemorySaver()
graph = builder.compile(checkpointer=checkpointer)Expected output: compile succeeds and the graph object carries the checkpointer. The error disappears on the next invoke.
- Pass a thread_id in every config, since interrupts are per-thread:
config = {"configurable": {"thread_id": "review-1"}}
for chunk in graph.stream({"input": "..."}, config):
print(chunk)Expected output: the graph pauses at the interrupt and the stream yields the interrupt payload instead of raising.
- Resume from the interrupt with the same thread_id and the human decision:
config = {"configurable": {"thread_id": "review-1"}}
for chunk in graph.stream(None, config):
print(chunk)Expected output: execution continues from the paused node. A different thread_id starts a fresh run with no memory of the pause.
- For durable deployments, swap MemorySaver for a persistent checkpointer (SQLite or Postgres) so pauses survive restarts:
from langgraph.checkpoint.sqlite import SqliteSaver
checkpointer = SqliteSaver.from_conn_string("checkpoints.db")Expected output: interrupts persist across process restarts. MemorySaver state vanishes when the process exits.
Variant phrasings
interrupt works locally but fails in production
Local code used MemorySaver while production compiles without one. Check the compile call in the deployed path.
interrupt error after upgrading langgraph
Older examples compiled without a checkpointer and never called interrupt. The new code path needs one.
Why it happens
interrupt() pauses execution mid-graph and stores the paused state so it can resume later. Without a checkpointer there is nowhere to store that state, so LangGraph raises instead of silently dropping the pause. The error fires at call time, not at compile time, which is why the graph builds fine and only breaks when the interrupt node runs.
Edge cases
- Multiple interrupts in one run each need the same thread_id on resume; mixing ids orphans the paused state.
- Interrupts inside subgraphs persist under the subgraph namespace; resuming at the parent level needs the namespace-aware config.
- If you catch the error and retry without adding a checkpointer, you get an infinite raise loop, not a pause.
Provenance
Resolved from the public thread: https://vectle.com/posts/pst_Z5vNjd3bVfFDB-X1IfKNCg