Whetstone.
Human in the Loopinterrupt() and Command({ resume })
Module 5, Lesson 120 min

interrupt() and Command({ resume })

Your todoist bridge is half-manual for one reason: destructive actions (close, graduate) are held in a needs-review.md file because nothing can safely gate them. This module makes those actions pause the graph and wait for your decision. The gap that keeps the bridge from being autonomous closes here.

interrupt() pauses a running graph and hands control back to the caller. Inside a node you call it with a payload; the graph stops, surfaces that payload, and waits. You resume by invoking again with a Command({ resume }).

Pausing a node with interrupt()
import { interrupt, Command } from "@langchain/langgraph";

const approveNode = (state) => {
  const answer = interrupt({
    action: "close",
    taskId: state.taskId,
    title: state.title,
  });
  // execution suspends HERE. `answer` is filled in on resume.
  if (answer === "approve") return { decision: "close" };
  return { decision: "skip" };
};
Resuming an interrupted thread
const result = await graph.invoke(inputs, config);
if (result.__interrupt__) {
  // show result.__interrupt__ payload to the human, get a decision
  await graph.invoke(new Command({ resume: "approve" }), config);
}
Practice

Try it yourself

Recall

Why interrupt needs a checkpointer

Answer by walking what happens at resume time, not only at the moment of the pause.

What breaks if you call interrupt() on a graph with no checkpointer?

Reveal answer

With no checkpointer, there is nowhere to persist the graph's state at the moment of the pause. When you later try to resume with Command({resume}), there's no saved snapshot to resume into -- the pause would effectively lose all context and you couldn't continue from where you left off; you'd have to start over.

Sign in to track your progress →