← Blog

Approvals & policy · · 4 min read

Why does a LangGraph approval step run again when I resume it?

Understand interrupt replay, repeated side effects, thread IDs, parallel approvals, and incomplete state when resuming human-in-the-loop agents.

Deixic

You approve an action, resume the agent, and watch an earlier step execute again. The approval screen looked like a pause button, but the runtime restores execution from a saved boundary, and that reruns code.

A LangGraph issue about steps re-running inside an interrupt loop makes the confusion concrete, and developers also ask about human review in concurrent subgraphs. An approval workflow has to account for both replay and the identity of the decision being resumed.

Why does code before interrupt execute again?#

LangGraph's interrupt documentation explains that resuming restarts the interrupted node, so code before the interrupt runs again. A saved checkpoint is no instruction pointer inside arbitrary application code.

Inspect the node from its first line to the interrupt and mark network writes, emails, inserts, and other external effects. Logging a line twice is a debugging clue, and sending a customer reply twice is a business consequence.

Move the proposed action into durable state before review, give execution its own step, and keep pre-review work safe to repeat. The approval UI then displays the saved proposal and never rebuilds a different one on each resume.

How do I stop an approval resume from repeating a side effect?#

Make side effects safe under replay and crash recovery. The graph API documentation describes re-execution and idempotency. A write placed after an interrupt leaves a crash window.

Persist an operation identity before dispatch, reuse it for retries when the provider supports idempotency, and save the returned result. If the process loses the response, reconcile instead of assuming the action did not run.

An approved ticket note, for example, has one logical operation tied to the ticket, note content version, and decision. Two worker attempts refer to that same operation.

Why does the graph resume the wrong conversation or start over?#

Check the thread identifier and persistence configuration used at the original interrupt and at resume. The interrupt guide requires a checkpointer and uses the thread ID to identify saved state, so a new thread identifier selects different state.

In a service handling many users, resolve the thread from authenticated application data, because a client-supplied thread ID must never expose another user's pending action. Map each approval identifier to the task and action behind it.

Test a restart between proposal and approval, since an in-memory checkpointer does not show that another process recovers the state. Also test two browser tabs approving the same item: the second submission retrieves the existing decision or is rejected as already handled.

How do I handle multiple approvals in parallel subgraphs?#

Route the response to the specific pending interrupt. Use the interrupt identifiers and resume interface documented for your installed version, never a global “yes” variable shared by all workers. The approval UI shows the action, target, and branch of work for each pending decision.

In your integration test, create two proposed changes with different targets, approve one, and deny the other. Assert the state transitions and the external operations attempted, then reverse the response order. The result depends on which action was approved, not which response arrived first.

Does an empty next field prove the task is finished?#

Check the runtime version and the rest of the saved state before relying on one field. A public multiple-interrupt report describes an empty next value while a second interrupt is pending. Reproduce it against your deployed version.

Define completion by the requested outcome and persisted action results, and keep pending approval, denied, executed, and outcome unknown distinguishable in your UI and storage.

State What the user should see
Waiting for review The exact proposed action and its target
Denied The decision and any permitted next step
Approved, pending Approval recorded; action unconfirmed
Executed Provider result and the relevant object reference
Outcome unknown The unresolved attempt and needed reconciliation

Keep a small replay suite: approval after restart, duplicate approval, denial, changed target, two parallel decisions, and a lost provider response. Each case tests whether the approval controls the real action.

Deixic's approvals let the person operating agents inspect a proposal, decide in the app queue or in Slack, and see the result in a tamper-evident history of decisions and results. A decision stays attached to its action.

Find another symptom in the agent troubleshooting question index.