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
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
history = list(graph.get_state_history(config)) # every saved checkpoint, newest first
snapshot = history[2] # pick one earlier stepget_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
graph.invoke(None, snapshot.config) # replay from that step; the nodes after it run againPassing 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
fork = graph.update_state(snapshot.config, {"topic": "chickens"}) # new branch with an edited value
graph.invoke(None, fork) # continue on that branchupdate_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.
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 100What replay and fork produced
get_state_historyreturns the checkpoints newest first; each has aconfigthat points at that exact step.- Invoking with
Noneand a snapshot's config replays from that step; the nodes before it are not re-run, the ones after it are. update_statemakes a new branch with an edited value instead of rolling back, so the original history stays intact.
Replay vs fork
| Replay | Fork (update_state) | |
|---|---|---|
| Changes the state | No | Yes, at that point |
| Original history | Kept | Kept; a new branch is added |
| Use for | Re-running from a step | Trying 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.
Related
- Previous: Streaming: watch a run happen
- Next: Retry policies: recovering from a failing node
- Reference: Time travel
- Print the
.nextof 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.