LangGraphLangGraph 1.2 · Python 3.10+
0%
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
38 small wins to finish your pathNext lesson →

Time travel: replay a past run

get_state_history lists every saved checkpoint of a thread. Pass a past checkpoint's config back to invoke to replay from that point; the nodes after it run again.

Last updated: 27 Sep, 2026 · LangGraph 1.2

Because a checkpointer saves the state after every step, you can go back to any step and continue from there, either to debug a run or to try a different path.

The get_state_history and update_state API

python
history = list(graph.get_state_history(config))    # newest first
snapshot = history[2]                               # some earlier checkpoint
graph.invoke(None, snapshot.config)                # replay from there

# fork: edit the state at that point and continue on a new branch
fork = graph.update_state(snapshot.config, {"topic": "chickens"})
graph.invoke(None, fork)

Two things happen here: replaying from a past step, then forking a new branch from it. Build them one at a time. Each snippet assumes graph was compiled with a checkpointer and has already run once on config.

Listing the checkpoints

python
history = list(graph.get_state_history(config))   # every saved checkpoint, newest first
snapshot = history[2]                              # pick one earlier step

get_state_history returns the saved checkpoints for the thread, newest first. Each one carries a config that points at that exact step. Here you take the third one back.

Replaying from a step

python
graph.invoke(None, snapshot.config)   # replay from that step; the nodes after it run again

Passing None as the input together with a snapshot's config replays from that step. The steps before it are not run again; the ones after it are.

Forking a new branch

python
fork = graph.update_state(snapshot.config, {"topic": "chickens"})  # new branch with an edited value
graph.invoke(None, fork)                                           # continue on that branch

update_state writes a changed value at that step and returns a config for a new branch. Invoking with it continues from the edited point, and the original history stays as it was.

Replay and fork in a run

A tiny graph with a checkpointer, run once, then replayed from an earlier step and forked onto a new branch.

Example
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START, END
from langgraph.checkpoint.memory import InMemorySaver

class State(TypedDict):
    n: int

def a(s): return {"n": s["n"] + 1}
def b(s): return {"n": s["n"] + 10}

builder = StateGraph(State)
builder.add_node("a", a)
builder.add_node("b", b)
builder.add_edge(START, "a")
builder.add_edge("a", "b")
builder.add_edge("b", END)
graph = builder.compile(checkpointer=InMemorySaver())

config = {"configurable": {"thread_id": "1"}}
print(graph.invoke({"n": 0}, config)["n"])              # first run: 0 -> +1 -> +10

history = list(graph.get_state_history(config))         # every checkpoint, newest first
before_b = next(s for s in history if s.next == ("b",)) # the checkpoint right before b runs
print(graph.invoke(None, before_b.config)["n"])         # replay from there: b runs again

fork = graph.update_state(before_b.config, {"n": 100})  # edit n at that step, new branch
print(graph.invoke(None, fork)["n"])                    # continue on the branch: b runs on 100

What replay and fork produced

  • get_state_history returns the checkpoints newest first; each has a config that points at that exact step.
  • Invoking with None and a snapshot's config replays from that step; the nodes before it are not re-run, the ones after it are.
  • update_state makes a new branch with an edited value instead of rolling back, so the original history stays intact.

Replay vs fork

ReplayFork (update_state)
Changes the stateNoYes, at that point
Original historyKeptKept; a new branch is added
Use forRe-running from a stepTrying a different value from a step

When to replay or fork

  • Debugging: jump back to the step before a bad result and watch it again.
  • Exploring: change one value partway through and see how the rest plays out.
Watch out. Replay re-runs the later nodes, it does not read from a cache. If a node after that point sends an email or charges a card, it happens again. Safe for exploring, careful in production.
Try it yourself
  • Print the .next of each snapshot to see which node runs next from it.
  • Fork from an early checkpoint with a changed value and compare the two runs.

This is what real progress feels like.