Unit 06.00: Why a long run needs to survive a restart
Without a checkpointer a graph is a function call: it runs, returns, and forgets. That is fine until a run needs to outlive the request that started it.
A thread id is the identity of one run
A checkpointer saves the state after each step, keyed by a thread id. A later call with the same thread id continues where the previous one stopped.
The code invokes twice on the same thread and reads the state back.
from typing import TypedDict
from langgraph.graph import StateGraph, START, END
from langgraph.checkpoint.memory import InMemorySaver
class State(TypedDict):
step: int
log: list
def work(state: State):
return {"step": state["step"] + 1, "log": state["log"] + [f"step {state['step'] + 1}"]}
g = StateGraph(State)
g.add_node("work", work)
g.add_edge(START, "work")
g.add_edge("work", END)
app = g.compile(checkpointer=InMemorySaver())
config = {"configurable": {"thread_id": "order-8841"}}
app.invoke({"step": 0, "log": []}, config)
app.invoke(None, config) # a later call on the same thread resumes
print("state after two invocations:", app.get_state(config).values)
# `thread_id` is the identity of one run. Without a checkpointer the graph is
# stateless between calls, so a process restart -- or simply a second HTTP
# request -- starts from nothing and repeats every step that already happened.
The second invoke passes None as input. It is not starting a new run with empty state - it is resuming the existing one, which is why the step count carries forward.
thread_id is the design decision here. It should be something meaningful in your domain - an order id, a ticket id - because that is what lets you find a run later without a lookup table. order-8841 is a thread you can search for during an incident.
The mistake this prevents
The mistake is treating persistence as an operational concern to add before launch. It changes what nodes must do: once a run can resume, every node has to be correct when re-entered with partial state, which is a design property rather than a configuration flag.
Takeaway
A checkpointer plus a thread id makes a run durable and resumable. Choose thread ids that mean something in your domain, and design nodes on the assumption they may be re-entered.
