> ## Documentation Index
> Fetch the complete documentation index at: https://docs.langchain.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Use time-travel

> Replay past executions and fork to explore alternative paths in LangGraph

## Overview

LangGraph supports time travel through [checkpoints](/oss/javascript/langgraph/persistence#checkpoints):

* **[Replay](#replay)**: Retry from a prior checkpoint.
* **[Fork](#fork)**: Branch from a prior checkpoint with modified state to explore an alternative path.

Both work by resuming from a prior checkpoint. Nodes before the checkpoint are not re-executed (results are already saved). Nodes after the checkpoint re-execute, including any LLM calls, API requests, and [interrupts](/oss/javascript/langgraph/interrupts) (which may produce different results).

## Replay

Invoke the graph with a prior checkpoint's config to replay from that point.

<Warning>
  Replay re-executes nodes—it doesn't just read from cache. LLM calls, API requests, and [interrupts](/oss/javascript/langgraph/interrupts) fire again and may return different results. Replaying from the final checkpoint (no `next` nodes) is a no-op.
</Warning>

<img src="https://mintcdn.com/langchain-5e9cc07a/dL5Sn6Cmy9pwtY0V/oss/images/re_play.png?fit=max&auto=format&n=dL5Sn6Cmy9pwtY0V&q=85&s=d7b34b85c106e55d181ae1f4afb50251" alt="Replay" width="2276" height="986" data-path="oss/images/re_play.png" />

Use [`getStateHistory`](https://reference.langchain.com/javascript/classes/_langchain_langgraph.pregel.Pregel.html#getStateHistory) to find the checkpoint you want to replay from, then call [`invoke`](https://reference.langchain.com/javascript/classes/_langchain_langgraph.index.CompiledStateGraph.html#invoke) with that checkpoint's config:

```typescript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
import { v7 as uuid7 } from "uuid";
import { StateGraph, MemorySaver, START } from "@langchain/langgraph";

const StateAnnotation = Annotation.Root({
  topic: Annotation<string>(),
  joke: Annotation<string>(),
});

function generateTopic(state: typeof StateAnnotation.State) {
  return { topic: "socks in the dryer" };
}

function writeJoke(state: typeof StateAnnotation.State) {
  return { joke: `Why do ${state.topic} disappear? They elope!` };
}

const checkpointer = new MemorySaver();
const graph = new StateGraph(StateAnnotation)
  .addNode("generateTopic", generateTopic)
  .addNode("writeJoke", writeJoke)
  .addEdge(START, "generateTopic")
  .addEdge("generateTopic", "writeJoke")
  .compile({ checkpointer });

// Step 1: Run the graph
const config = { configurable: { thread_id: uuid7() } };
const result = await graph.invoke({}, config);

// Step 2: Find a checkpoint to replay from
const states = [];
for await (const state of graph.getStateHistory(config)) {
  states.push(state);
}

// Step 3: Replay from a specific checkpoint
const beforeJoke = states.find((s) => s.next.includes("writeJoke"));
const replayResult = await graph.invoke(null, beforeJoke.config);
// writeJoke re-executes (runs again), generateTopic does not
```

## Fork

Fork creates a new branch from a past checkpoint with modified state. Call [`update_state`](https://reference.langchain.com/javascript/classes/_langchain_langgraph.pregel.Pregel.html#updateState) on a prior checkpoint to create the fork, then [`invoke`](https://reference.langchain.com/javascript/classes/_langchain_langgraph.index.CompiledStateGraph.html#invoke) with `None` to continue execution.

<img src="https://mintcdn.com/langchain-5e9cc07a/-_xGPoyjhyiDWTPJ/oss/images/checkpoints_full_story.jpg?fit=max&auto=format&n=-_xGPoyjhyiDWTPJ&q=85&s=a52016b2c44b57bd395d6e1eac47aa36" alt="Fork" width="3705" height="2598" data-path="oss/images/checkpoints_full_story.jpg" />

<Warning>
  `update_state` does **not** roll back a thread. It creates a new checkpoint that branches from the specified point. The original execution history remains intact.
</Warning>

```typescript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
// Find checkpoint before writeJoke
const states = [];
for await (const state of graph.getStateHistory(config)) {
  states.push(state);
}
const beforeJoke = states.find((s) => s.next.includes("writeJoke"));

// Fork: update state to change the topic
const forkConfig = await graph.updateState(
  beforeJoke.config,
  { topic: "chickens" },
);

// Resume from the fork — writeJoke re-executes with the new topic
const forkResult = await graph.invoke(null, forkConfig);
console.log(forkResult.joke); // A joke about chickens, not socks
```

### From a specific node

When you call [`update_state`](https://reference.langchain.com/javascript/classes/_langchain_langgraph.pregel.Pregel.html#updateState), values are applied using the specified node's writers (including [reducers](/oss/javascript/langgraph/graph-api#reducers)). The checkpoint records that node as having produced the update, and execution resumes from that node's successors.

By default, LangGraph infers `as_node` from the checkpoint's version history. When forking from a specific checkpoint, this inference is almost always correct.

Specify `as_node` explicitly when:

* **Parallel branches**: Multiple nodes updated state in the same step, and LangGraph can't determine which was last (`InvalidUpdateError`).
* **No execution history**: Setting up state on a fresh thread (common in [testing](/oss/javascript/langgraph/test)).
* **Skipping nodes**: Set `as_node` to a later node to make the graph think that node already ran.

```typescript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
// graph: generateTopic -> writeJoke

// Treat this update as if generateTopic produced it.
// Execution resumes at writeJoke (the successor of generateTopic).
const forkConfig = await graph.updateState(
  beforeJoke.config,
  { topic: "chickens" },
  { asNode: "generateTopic" },
);
```

## Interrupts

If your graph uses [`interrupt`](https://reference.langchain.com/javascript/langchain-langgraph/index/interrupt) for [human-in-the-loop](/oss/javascript/langgraph/interrupts) workflows, interrupts are always re-triggered during time travel. The node containing the interrupt re-executes, and `interrupt()` pauses for a new `Command(resume=...)`.

```typescript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
import { interrupt, Command } from "@langchain/langgraph";

function askHuman(state: { value: string[] }) {
  const answer = interrupt("What is your name?");
  return { value: [`Hello, ${answer}!`] };
}

function finalStep(state: { value: string[] }) {
  return { value: ["Done"] };
}

// ... build graph with checkpointer ...

// First run: hits interrupt
await graph.invoke({ value: [] }, config);
// Resume with answer
await graph.invoke(new Command({ resume: "Alice" }), config);

// Replay from before askHuman
const states = [];
for await (const state of graph.getStateHistory(config)) {
  states.push(state);
}
const beforeAsk = states.filter((s) => s.next.includes("askHuman")).pop();

const replayResult = await graph.invoke(null, beforeAsk.config);
// Pauses at interrupt — waiting for new Command({ resume: ... })

// Fork from before askHuman
const forkConfig = await graph.updateState(beforeAsk.config, { value: ["forked"] });
const forkResult = await graph.invoke(null, forkConfig);
// Pauses at interrupt — waiting for new Command({ resume: ... })

// Resume the forked interrupt with a different answer
await graph.invoke(new Command({ resume: "Bob" }), forkConfig);
// Result: { value: ["forked", "Hello, Bob!", "Done"] }
```

### Multiple interrupts

If your graph collects input at several points (for example, a multi-step form), you can fork from between the interrupts to change a later answer without re-asking earlier questions.

```typescript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
// Fork from BETWEEN the two interrupts (after askName, before askAge)
const states = [];
for await (const state of graph.getStateHistory(config)) {
  states.push(state);
}
const between = states.filter((s) => s.next.includes("askAge")).pop();

const forkConfig = await graph.updateState(between.config, { value: ["modified"] });
const result = await graph.invoke(null, forkConfig);
// askName result preserved ("name:Alice")
// askAge pauses at interrupt — waiting for new answer
```

## Subgraphs

Time travel with [subgraphs](/oss/javascript/langgraph/use-subgraphs) depends on whether the subgraph has its own checkpointer. This determines the granularity of checkpoints you can time travel from.

<Tabs>
  <Tab title="Inherited checkpointer (default)">
    By default, a subgraph inherits the parent's checkpointer. The parent treats the entire subgraph as a **single super-step** — there is only one parent-level checkpoint for the whole subgraph execution. Time traveling from before the subgraph re-executes it from scratch.

    You cannot time travel to a point *between* nodes in a default subgraph — you can only time travel from the parent level.

    ```typescript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
    // Subgraph without its own checkpointer (default)
    const subgraph = new StateGraph(StateAnnotation)
      .addNode("stepA", stepA)       // Has interrupt()
      .addNode("stepB", stepB)       // Has interrupt()
      .addEdge(START, "stepA")
      .addEdge("stepA", "stepB")
      .compile();  // No checkpointer — inherits from parent

    const graph = new StateGraph(StateAnnotation)
      .addNode("subgraphNode", subgraph)
      .addEdge(START, "subgraphNode")
      .compile({ checkpointer });

    // Complete both interrupts
    await graph.invoke({ value: [] }, config);
    await graph.invoke(new Command({ resume: "Alice" }), config);
    await graph.invoke(new Command({ resume: "30" }), config);

    // Time travel from before the subgraph
    const states = [];
    for await (const state of graph.getStateHistory(config)) {
      states.push(state);
    }
    const beforeSub = states.filter((s) => s.next.includes("subgraphNode")).pop();

    const forkConfig = await graph.updateState(beforeSub.config, { value: ["forked"] });
    const result = await graph.invoke(null, forkConfig);
    // The entire subgraph re-executes from scratch
    // You cannot time travel to a point between stepA and stepB
    ```
  </Tab>

  <Tab title="Subgraph checkpointer">
    Set `checkpointer=True` on the subgraph to give it its own checkpoint history. This creates checkpoints at each step **within** the subgraph, allowing you to time travel from a specific point inside it — for example, between two interrupts.

    Use [`get_state`](https://reference.langchain.com/javascript/classes/_langchain_langgraph.pregel.Pregel.html#getState) with `subgraphs=True` to access the subgraph's own checkpoint config, then fork from it:

    ```typescript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
    // Subgraph with its own checkpointer
    const subgraph = new StateGraph(StateAnnotation)
      .addNode("stepA", stepA)       // Has interrupt()
      .addNode("stepB", stepB)       // Has interrupt()
      .addEdge(START, "stepA")
      .addEdge("stepA", "stepB")
      .compile({ checkpointer: true });  // Own checkpoint history

    const graph = new StateGraph(StateAnnotation)
      .addNode("subgraphNode", subgraph)
      .addEdge(START, "subgraphNode")
      .compile({ checkpointer });

    // Run until stepA interrupt, then resume -> hits stepB interrupt
    await graph.invoke({ value: [] }, config);
    await graph.invoke(new Command({ resume: "Alice" }), config);

    // Get the subgraph's own checkpoint (between stepA and stepB)
    const parentState = await graph.getState(config, { subgraphs: true });
    const subConfig = parentState.tasks[0].state.config;

    // Fork from the subgraph checkpoint
    const forkConfig = await graph.updateState(subConfig, { value: ["forked"] });
    const result = await graph.invoke(null, forkConfig);
    // stepB re-executes, stepA's result is preserved
    ```
  </Tab>
</Tabs>

See [subgraph persistence](/oss/javascript/langgraph/use-subgraphs#subgraph-persistence) for more on configuring subgraph checkpointers.

***

<div className="source-links">
  <Callout icon="terminal-2">
    [Connect these docs](/use-these-docs) to Claude, VSCode, and more via MCP for real-time answers.
  </Callout>

  <Callout icon="edit">
    [Edit this page on GitHub](https://github.com/langchain-ai/docs/edit/main/src/oss/langgraph/use-time-travel.mdx) or [file an issue](https://github.com/langchain-ai/docs/issues/new/choose).
  </Callout>
</div>
