Skip to course content
Free LangGraph course

LangGraph for Agentic Workflows

Unit 10.02: Reproducing a failure from a checkpoint

A bug you can only observe in production is a bug you debug by guessing. The recorded inputs are what turn it into something you can run.

Replay on a fresh thread with the same inputs

The checkpoint holds the inputs a run started with. Replaying means invoking the same graph on a new thread with those exact values, changing nothing else.

The code runs a graph, reads its recorded state, and replays it.

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


class State(TypedDict):
    value: int
    trail: list


def step_a(state: State):
    return {"value": state["value"] + 1, "trail": state["trail"] + ["a"]}


def step_b(state: State):
    return {"value": state["value"] * 10, "trail": state["trail"] + ["b"]}


g = StateGraph(State)
g.add_node("a", step_a)
g.add_node("b", step_b)
g.add_edge(START, "a")
g.add_edge("a", "b")
g.add_edge("b", END)
app = g.compile(checkpointer=InMemorySaver())

original = {"configurable": {"thread_id": "prod-run"}}
app.invoke({"value": 4, "trail": []}, original)
failed_state = app.get_state(original).values
print("production run ended as:", failed_state)

# Replay it on a fresh thread with the exact inputs, changing nothing else.
replay = {"configurable": {"thread_id": "replay-1"}}
print("replayed             :", app.invoke({"value": 4, "trail": []}, replay))

# Reproducing from the recorded inputs is the whole reason to store them. A bug
# you can only observe in production is a bug you debug by guessing.

The replay produces the same result, which confirms the failure is deterministic given those inputs - a genuinely useful thing to establish early. If it had not reproduced, that is equally informative: the failure depends on something outside the recorded state, which narrows the search to external calls, time, or randomness.

Using a fresh thread_id matters. Replaying onto the original thread overwrites the evidence you are trying to investigate.

The mistake this prevents

The mistake is changing something while reproducing - a smaller input, a different model, an extra log line inside the node. You then learn whether the modified thing fails, which is a different question. Reproduce exactly first, then start changing one thing at a time.

Takeaway

Replay from the recorded inputs on a fresh thread, changing nothing. Whether it reproduces is itself the first useful finding: deterministic means it is in your code, non-deterministic means it is outside the state.