Unit 11.03: Checkpointing and resuming a real run
The last structural requirement is that the run survives being put down and picked up, without repeating the work it already did.
Same graph, same thread, no inputs
Resuming is invoke(None, config) against the same thread id. The checkpointer supplies the state; nothing is re-derived.
The code pauses a research run, resumes it, and counts the fetches.
from typing import TypedDict
from langgraph.graph import StateGraph, START, END
from langgraph.checkpoint.memory import InMemorySaver
FETCHES = []
class State(TypedDict):
sources: list
summary: str
def research(state: State):
FETCHES.append(1)
return {"sources": ["s1", "s2", "s3"]}
def summarise(state: State):
return {"summary": f"{len(state['sources'])} sources reviewed"}
g = StateGraph(State)
g.add_node("research", research)
g.add_node("summarise", summarise)
g.add_edge(START, "research")
g.add_edge("research", "summarise")
g.add_edge("summarise", END)
app = g.compile(checkpointer=InMemorySaver(), interrupt_before=["summarise"])
config = {"configurable": {"thread_id": "resume-demo"}}
app.invoke({"sources": [], "summary": ""}, config)
print("after pause :", app.get_state(config).values)
# Simulating a restart: the same compiled graph, the same thread_id, no inputs.
print("after resume:", app.invoke(None, config))
print(f"research ran {len(FETCHES)} time(s) across both calls")
research ran once across both calls. The resume picked up at summarise with the sources already in state.
This is the property that makes the human gate practical rather than theoretical. The pause between the two calls can be a day, can span a deployment, and can be resumed by a different process - and the expensive research step is not repeated in any of those cases.
The mistake this prevents
The mistake is testing resume within a single process and calling it verified. That test passes even when nodes depend on module-level state that would not survive a restart. Verify by resuming from a genuinely new process, which is the condition production will actually present.
Takeaway
Resume with the same thread id and no inputs, and assert that expensive nodes ran once. Verify from a fresh process - an in-process test cannot detect dependence on state that would not survive a restart.
