That second run is expected: when LangGraph resumes an interrupted graph, it starts the node containing interrupt() from the beginning. The resumed call to interrupt() returns the value passed through Command(resume=...), and the node continues from there. So any code before the interrupt runs again; it is not, by itself, evidence of an accidental graph loop.
What happens when LangGraph resumes an interrupt?
interrupt() pauses graph execution and surfaces a payload for an external caller, such as a human approval step. To resume, the caller invokes the graph with Command(resume=value). LangGraph re-enters the interrupted node from its first statement; this time, the call to interrupt() returns value, and execution proceeds after it. The official LangGraph interrupt guide describes this restart behavior.
As an Amazon Associate I earn from qualifying purchases.
For example, the request-building line below runs on both the initial attempt and the resumed attempt. The code after interrupt() runs once the resume value is returned.
from langgraph.types import Command, interrupt
def approval_node(state):
# Runs on the initial attempt and again when the node resumes.
request = build_approval_request(state)
approved = interrupt(request)
# Runs after the resume value is returned.
return {"approved": approved}
# Initial invocation pauses at interrupt().
result = graph.invoke(
input_data,
config={"configurable": {"thread_id": "case-123"}},
)
# Resume the paused thread with the same thread ID.
result = graph.invoke(
Command(resume=True),
config={"configurable": {"thread_id": "case-123"}},
)
What does the resumed call need?
A checkpointer must be configured to persist the graph state, and the resume invocation must use the same thread_id as the paused invocation. The checkpointer uses that thread to locate the saved execution. If you resume with a different thread ID, you are addressing a different thread rather than continuing the paused one. See the official interrupt documentation for the interrupt and resume pattern.
#1 Best Overall
How can you prevent duplicate side effects?
Focus on externally visible work in the interrupted node before interrupt(). Because that code runs again on resume, it could repeat a database write, message send, payment, or external API call. Earlier nodes are represented by checkpointed graph progress; the specific restart concern is the code in the node that contains the interrupt.
- Keep pre-interrupt work free of external side effects when practical. Building a request or validating state is safer than committing a change.
- Make necessary pre-interrupt operations idempotent. An application-level idempotency key can help ensure repeated attempts do not create duplicate effects; this is an application design technique, not a LangGraph guarantee.
- Move the side effect after the interrupt if it should happen only once approval or other input has arrived.
- Put the effect in a separate node when that makes its execution boundary clearer.
What if the node has several interrupts or exception handling?
Keep multiple interrupts in the same order
If one node calls interrupt() more than once, preserve the call order across the original and resumed executions. LangGraph matches resume values by position, so changing the order can associate a value with the wrong interrupt. The Python API reference documents the interrupt API and its behavior.
Do not swallow the pause signal
An interrupt uses a special control-flow exception that LangGraph handles to pause execution. Avoid wrapping interrupt() in a broad try/except that could catch and suppress that signal. Keep handling for ordinary application errors separate from the interrupt call; consult the interrupt guide for the framework’s guidance.
How to tell a restart from an accidental loop
If the repeated log entries come from statements before interrupt() in the interrupted node, they are consistent with normal resume behavior. To investigate a genuine loop, trace which node executes after the interrupt returns and inspect the graph’s routing and state updates. The restart rule alone does not show that every node in the workflow runs again.
Rank #3
LangGraph’s interrupt behavior can vary with framework versions. Check the documentation corresponding to the version installed in your application if the observed behavior differs from this pattern.
Quick Recap
Rank #4
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




