Pause and resume LangGraph approval without duplicate actions
A local rejection/resumption example and explicit durability, identity and deduplication requirements.
Use caseUse interrupt and Command for approval, rejection and resumption. Distinguish thread IDs, in-memory checkpoints and durable storage, with an audit-record checklist.
Use interrupt and Command for approval, rejection and resumption. Distinguish thread IDs, in-memory checkpoints and durable storage, with an audit-record checklist.
Source · JevLog editorial · Original guide

Source cover · LangChain
Published 2026-10-02 from current official docs with original exercises. No live service calls; source publication dates not established.
Problem
Section titled “Problem”AI workflows need a place to wait for review. A pause is not authorization; applications validate identity, proposal version and permission. Resumption can restart node code, requiring side-effect protection.
Before you begin
Section titled “Before you begin”-
Install Python and langgraph and download approval-record.json. The example returns state without external business calls.
-
InMemorySaver is a single-process exercise; restart recovery needs a durable checkpointer.
Original JevLog diagram, not a product screenshot or measured result.
1. Freeze proposal and version
Section titled “1. Freeze proposal and version”Show proposal ID, version, evidence and intended action. Recheck version server-side so viewing v1 cannot authorize v2. Start with a reversible internal draft.
2. Build pause and rejection
Section titled “2. Build pause and rejection”The first invoke pauses; Command resumes the same thread. Only explicit boolean True grants approval. This example rejects; begin a new thread for an approval exercise.
from typing import TypedDictfrom langgraph.graph import StateGraph, START, ENDfrom langgraph.checkpoint.memory import InMemorySaverfrom langgraph.types import interrupt, Command
class State(TypedDict): proposal: str status: str
def review(state: State): decision = interrupt({"proposal":state["proposal"], "question":"Approve this draft?"}) approved = isinstance(decision, dict) and decision.get("approved") is True return {"status":"approved" if approved else "rejected"}
builder = StateGraph(State)builder.add_node("review", review)builder.add_edge(START, "review")builder.add_edge("review", END)graph = builder.compile(checkpointer=InMemorySaver())config = {"configurable":{"thread_id":"demo-proposal-001"}}print(graph.invoke({"proposal":"Create an internal draft only","status":"pending"}, config))print(graph.invoke(Command(resume={"approved":False}), config))3. Inspect rejection and repeated approval
Section titled “3. Inspect rejection and repeated approval”A rejection terminates as rejected without actions. Completed tasks are not new approvals; duplicates read the existing outcome. Persist proposal version and reviewer server-side.
4. Move side effects after review
Section titled “4. Move side effects after review”Resumption restarts the interrupted node. Separate messaging, charging and creation into a later stage with proposal-and-version uniqueness. Do not swallow interruption exceptions.
5. Add durability and identity checks
Section titled “5. Add durability and identity checks”The in-memory example does not prove restart recovery. Add durable storage and audit identity, time, version, execution and failures. A thread ID locates state, not authorization; confidence cannot substitute for approval.
Practice sample: download approval-record.json for local use; not a live run here.
Result and cautions
Section titled “Result and cautions”A local rejection/resumption example and explicit durability, identity and deduplication requirements.
- Approval inputs can be stale; recheck proposal and version.
- Rejection blocks action and repeated approval cannot repeat it.
- Durability and server-side identity/version checks are specified.
Source boundary: compiled from the linked public sources; not reproduced here. Review classifications before acting; they do not run actions automatically.
Related guides
Section titled “Related guides”- Human review queue tutorial: low scores and missing labels
- Handle Jev Python timeouts, retries and error states
- Handle n8n failures with error queues and bounded retries
Can a thread ID authorize approval?
Section titled “Can a thread ID authorize approval?”No. It locates state; the server validates identity, permission, object and version.