LangGraph Interrupts: Hold a Library Bulk Email Until a Real Person Approves It

A fictional neighborhood library needs to notify patrons about a changed opening hour. An assistant can draft a message, but a correct-sounding draft must not become an authorized bulk email merely because the graph reached its final node. LangGraph interrupts provide a pause point where a person can review the exact message, audience, and schedule. This guide describes how to build that gate, what survives a restart, and why resuming a paused node needs special care.

Define the boundary between a suggestion and permission

Start by separating three states: proposed copy, approved copy, and dispatched mail. The agent may read a verified opening-hours notice, suggest a subject line, and count eligible subscribers. It must not treat a model-generated sentence such as ‘looks good’ as authorization. The reviewer needs to see the exact message and recipient segment, not a vague summary that conceals a missing date or a mistakenly included private list.

Write the approval policy before touching the graph. Name who can authorize a notice, whether two staff members are needed for emergency announcements, and where the approved hours come from. In the example, the library’s signed operations notice is authoritative. A chat history, an AI paraphrase, or an old event page is not. Store a stable draft identifier so the reviewer can identify precisely what is being approved.

Place an interrupt ahead of the external action

LangGraph documents an interrupt() call that pauses execution and surfaces a JSON-serializable payload to the caller. Put this pause before the node that dispatches the email, not after it. The payload can hold the draft ID, recipient-count estimate, approved hours reference, proposed subject, and a clearly labeled status of ‘needs staff review.’ It should omit unnecessary subscriber addresses from the approval screen.

The graph needs a checkpointer and a consistent thread identifier so a later invocation can find the paused state. In a production service, use durable storage instead of a throwaway in-memory object. Make the approval screen independent of the model: a human clicks an action associated with a versioned draft. The visual process below distinguishes draft creation, durable pause, staff decision, and guarded send.

Four stages: proposed library email, graph checkpoint pause, authenticated staff review, and guarded send. This is a conceptual teaching illustration, not measured service output.

Resume only the reviewed version

The documented resume mechanism passes a value using Command(resume=…) with the same thread identifier. Validate that input on the server: it should identify the acting reviewer, the decision, and the immutable draft version. A bare Boolean can illustrate the API, but a real authorization system must independently verify the person’s role and whether the draft changed after the review screen loaded.

If someone edits the opening hour while the task is waiting, make the previous approval invalid. A request to reject should return the work to drafting without executing the send action. Prefer a separate review-record store for who approved which version. The graph checkpoint answers where execution paused; it does not automatically prove that organizational authorization was valid.

Account for node replay and side effects

A crucial LangGraph caveat is that a resumed interrupt re-enters the node from its beginning. Code before the interrupt can run again. Therefore do not place an irreversible network send, billing operation, or record mutation before the pause in that same node. A reassuring progress log can be repeated; a public mailing must not be. Put side effects in a downstream node that checks both authorization and dispatch state.

For a hypothetical batch of 40 members, the wrong sequence is ‘send 40, then pause for approval.’ The reviewed sequence is ‘prepare 40, pause, verify one approval, then attempt one idempotent send.’ Use a dispatch key such as notice ID plus approved version and recipient batch, so retries can identify a completed action instead of creating another send. The comparison graphic shows the different outcomes rather than fabricated run results.

Incorrect send-before-approval branch versus reviewed approval-first branch with distinct mail outcomes. This is a conceptual teaching illustration, not measured service output.

Keep the draft, audience, and decision together

A paused graph can persist execution state, but related business records still need consistent IDs. Store the audience query and its cutoff time with the approved message. Recomputing a broad list after approval could add members whom the reviewer never intended to contact. Conversely, holding personal contact data in a checkpoint longer than necessary increases exposure. Design a separate, access-controlled mail service to resolve the authorized segment at dispatch.

Show a reviewer a dry-run preview with the full subject, opening-hours line, footer, and sender identity. If the message contains an address or phone number, compare it to the current library directory. Ask whether an accessibility accommodation line was omitted. Save the reviewer decision with a timestamp, but never represent that timestamp as proof the email was actually delivered.

Test reject, timeout, and duplicate resume

Test cases should include approval, rejection, draft replacement, missing reviewer permission, storage restart, and two near-simultaneous resume requests. The outcome to protect is not that the LLM wrote polite copy; it is that no unapproved mail leaves the service. Run the exercise using sandbox addresses and an isolated sending stub. Never test it against real patron contacts without explicit institutional authorization.

For each case, compare expected state with the downstream send counter and durable approval record. The correct rejection path must have zero sends; the approved path must not multiply sends when a worker restarts. If a checkpoint is corrupted or unavailable, fail closed and surface a staff escalation. A retry button should not silently re-authorize a different message.

Make the release screen show evidence

Before launch, inspect the authorization ledger and the actual mail-provider response separately. A provider response indicating acceptance is not proof that every patron received or read the notice. Label delivery statuses distinctly and keep bounce handling outside the approval mechanism. If the event is urgent, provide a human escalation path so a rate limit or broken worker cannot indefinitely hide the changed hours.

The final diagram highlights three gates: the approved message version, the authenticated reviewer, and one dispatch key. All three must match before the send path can execute. Keep the source notice available for human comparison, and publish the operational hours through the library’s normal verified channel. An agent should shorten clerical work, not acquire independent permission to contact the public.

Review console showing approved draft version, reviewer identity, and idempotent dispatch key. This is a conceptual teaching illustration, not measured service output.

Choose the right scope for an interrupt

An interrupt is useful when an agent can prepare consequential work but a human must decide whether it happens. It is not a substitute for ordinary validation of field types, a transaction boundary, or a secure permission service. If the activity is merely formatting internal notes, a review-in-draft step may be simpler than orchestration with persistent checkpoints and a resume UI.

Review the cost of storing checkpoints, the sensitivity of paused payloads, and whether the team has operators who can clear stuck tasks. Record what triggers an interrupt and how to cancel a pending task. The smallest implementation that enforces the real approval policy is generally more reliable than a complicated autonomous workflow whose actions nobody can audit.

How We Research

For this November 2026 article, we studied vendor-maintained primary documentation: LangGraph: Interrupts, LangGraph documentation source: interrupt and resume semantics. We translated the documented workflow and caveats into the fictional, limited-scope example above. We did not execute a paid cloud request, inspect private records, measure model accuracy, interview customers, or reproduce the provider UI. Any values shown in the diagrams are invented teaching labels, not benchmark results. Review release notes, account permissions, regional availability, processing terms, and pricing before using this process on production data.

Frequently Asked Questions

Does interrupt() automatically ask the right person for approval?

No. It pauses graph execution and exposes a value. Your application must build the review interface and verify the approver’s identity and authority.

Why does a node sometimes execute code twice around a pause?

On resume, LangGraph restarts the node that contained the interrupt. Keep external side effects after an appropriately guarded approval stage and make downstream actions idempotent.

What is needed to resume the same paused task?

Use a checkpointer and the original thread identifier, then supply a validated resume value with Command(resume=…). Do not accept an anonymous Boolean as production authorization.

Can the graph checkpoint prove an email was delivered?

No. It describes workflow state. Approval, mail-provider acceptance, delivery attempts, and recipient delivery are separate records with different meanings.

What happens when staff reject a draft?

Treat the draft as unapproved, skip the sending action, and require review of a new immutable version if the message is revised. Log that decision without implying that any mail was sent.

Scroll to Top