Walk through LangGraph time-travel debugging via the checkpointer
Same topic, related formats. Practice these next.
Same topic, related formats. Practice these next.
An agent built on LangGraph produced a bad final answer on thread `t-42`. The checkpointer is configured (Postgres). Walk through how you would use time-travel to find the bad decision, edit state at that point, and replay forward from there.
List checkpoints with `get_state_history`, find the bad one, fork by calling `update_state` with a pinned `checkpoint_id`, then re-invoke the graph to replay forward.
Picture a video game with auto-saves at every save point. Your character makes a wrong turn at level 4 and dies at level 7. You scroll the save list, pick the save just before the wrong turn, change the equipment in your inventory, and press resume. The game keeps both timelines on disk so you can compare endings. LangGraph plus a checkpointer works the same way. Every node creates a save point. You can list them, edit the saved state at any point, and resume from there. And the original timeline stays intact for comparison.
Everything you need to truly understand this topic: intuition, mechanics, step by step explanation, code, formulas, and worked example. Click to expand.
Everything you need to truly understand this topic: intuition, mechanics, step by step explanation, code, formulas, and worked example.
Everything important, quickly.
8 min: three APIs, pinned checkpoint_id fork semantics, re-invocation contract, checkpointer choices, parent_config DAG, why AgentExecutor cannot do this.
from langgraph.checkpoint.postgres import PostgresSaver
checkpointer = PostgresSaver.from_conn_string(POSTGRES_URL)
app = graph.compile(checkpointer=checkpointer)
cfg = {"configurable": {"thread_id": "t-42"}}
# 1) List history
history = list(app.get_state_history(cfg))
for snap in history:
print(snap.config["configurable"]["checkpoint_id"], snap.next, snap.values)
# 2) Pick the bad checkpoint, fork by pinning checkpoint_id
bad_id = history[3].config["configurable"]["checkpoint_id"]
new_cfg = app.update_state(
config={"configurable": {"thread_id": "t-42", "checkpoint_id": bad_id}},
values={"messages": history[3].values["messages"] + [corrected_msg]},
)
# 3) Replay forward from the corrected state
result = app.invoke(None, config=new_cfg)
Real products, models, and research that use this idea.
What an interviewer would ask next. Try answering before peeking at the approach.
Red flags and common mistakes that signal junior thinking. Click to expand.
Calling `update_state` without pinning a `checkpoint_id`. That mutates the latest state instead of forking, and you lose the ability to compare the original run against your corrected branch.
The night-before-the-interview bullets. Scan these on the way to the call.
Primary sources. Skim if you want the original framing.