AI Agent Checkpointing Explained: Durable State, Replay and Recovery
How checkpointing lets an agent survive crashes, wait for people and be replayed for debugging, using LangGraph as the worked example. Covers durability modes, resuming, replay traps, storage, encryption and retention.
By Naeem Akhtar · 13 min read
In short
- A checkpointer saves a snapshot of agent state after each step, grouped by thread. That is what makes pausing, resuming, recovery and replay possible.
- Durability mode is a trade-off. exit is fastest but loses mid-run state in a crash, async risks a small gap, and sync writes every checkpoint first.
- Replay re-runs nodes after the chosen checkpoint, including model calls, API calls and interrupts. Make tools safe to repeat before you debug with it.
- Checkpoints hold real data and grow without limit. Encrypt them, set retention, and keep your audit record separate.
What a checkpoint is, and why agents need one
An agent run is not one function call. It is a series of steps: read the request, call a model, call a tool, wait for a person, call the model again. If the process dies in step four, or a person takes two days to answer, a naive agent has lost everything it knew. Checkpointing fixes that by saving the agent's state as it goes, so work can stop and restart without starting over.
In LangGraph, a checkpointer saves a snapshot of the graph state at each super-step, grouped into threads. A super-step is one tick of the graph in which all nodes scheduled for that step run, possibly in parallel. For a simple graph that goes from start to node A to node B to end, the documentation says you get four checkpoints: the empty state, the input, the output of A, and the output of B. Each snapshot records the state values, which node runs next, a checkpoint ID, metadata about the step and a timestamp.
Those snapshots power four things the documentation calls out: human-in-the-loop pauses, memory across interactions, time travel for debugging, and fault tolerance. This guide uses LangGraph as the worked example because its documentation is detailed, then compares what other frameworks do. If you are new to the pause side, start with Pause an AI Agent for Human Approval with LangGraph interrupt(). Details here come from the LangGraph documentation checked on 10 November 2026, and some features, including delta channels, are marked beta or version-dependent, so confirm against the version you run.
Threads: the cursor into saved state
A thread is the identifier under which a sequence of checkpoints is saved. You pass it as thread_id in the run configuration, and the documentation says the checkpointer uses it as the primary key for storing and retrieving checkpoints. Without a thread ID, nothing can be saved or resumed.
Reusing a thread ID does not by itself mean a run resumes. The documentation is explicit that the input and checkpoint ID decide what happens:
| Goal | How you invoke it | What happens |
|---|---|---|
| Submit new input | Invoke with the input and the thread config | The input is applied to saved state using the state's reducers, and the run starts from the entry point |
| Continue after an error | Invoke with no input and the thread config | The latest checkpoint is loaded and unfinished work is retried |
| Answer a pause | Invoke with Command(resume=answer) | The interrupted node restarts from its beginning, with your answer returned from interrupt() |
| Replay history | Invoke with no input and a snapshot's config | Nodes after that checkpoint run again |
Two practical consequences follow. New input does not clear the thread, because it is applied to the existing state. To start with empty state you need a new thread ID. And a thread ID is the handle your own application must store, usually against a business record such as a ticket or a request, so that you can find the right saved run later.
Fault tolerance: what happens when a node fails
The documentation describes two levels of saving. Full snapshots are written at super-step boundaries. In addition, as each node finishes inside a super-step, its output is written as a pending write linked to the in-progress checkpoint. If another node in the same super-step then fails, the successful nodes' writes are already stored, so when you resume from that point they are not run again.
That matters for cost and for side effects. Imagine three tools running in parallel and one throws an error. Without pending writes, a retry repeats all three, including the two that already sent an email or called a paid API. With them, only the failed node reruns. It does not remove the need for idempotent tools, because a node can still fail after performing its action and before its result is recorded.
Durability modes: speed against safety
LangGraph lets you choose when checkpoints are written, per run. The three modes, from least to most durable, are described in the documentation as follows.
| Mode | When state is saved | Trade-off |
|---|---|---|
| exit | Only when the run exits, whether it succeeds, errors or pauses for a person | Best performance for long runs, but intermediate state is not saved, so a process crash mid-run cannot be recovered |
| async | Asynchronously, while the next step runs | Good performance and durability, with a small risk that a checkpoint is not written if the process crashes |
| sync | Synchronously, before the next step starts | Every checkpoint is written before continuing, at the cost of some overhead |
# Highest durability: write each checkpoint before the next step runs
graph.stream({"input": "test"}, config, durability="sync")Choose by what a lost step costs. An agent that issues refunds or moves records should use sync, because losing the knowledge that a step happened is worse than a few milliseconds. A read-only research agent that can simply start again may be happy with exit or async. For approval workflows, note that exit still saves state when the run pauses for a person, but not between steps before that.
The gap before the first checkpoint
One caveat in the documentation is easy to miss and important for systems that must not lose requests. For a new thread, the mode decides what is committed before the first node runs. With exit, no checkpoint exists until the run ends. With async, the write starts but the first node can run before it completes. With sync, the input checkpoint and the step zero checkpoint are committed first.
In every mode, the documentation warns, a crash before the first checkpoint becomes durable leaves the thread indistinguishable from one that was never invoked. Looking up the thread returns nothing, and invoking it with no input raises an empty-input error. If you need to tell 'never accepted' from 'accepted but lost', record admission yourself, outside the checkpointer. The documentation suggests committing an outbox row to your own store before you call the graph. That row is your proof that a request arrived.
Inspecting, editing and replaying state
Because every step is saved, you can look back. Three operations matter in practice.
- Get state: fetch the latest snapshot for a thread, or a specific checkpoint, to see the values, what runs next, and the metadata.
- Get state history: list every snapshot for a thread, newest first. You can filter it to find the checkpoint before a particular node, the step where an interrupt occurred, or checkpoints created by an edit.
- Update state: edit the state. This creates a new checkpoint rather than changing the original, and the values go through the state's reducers, so fields with a reducer accumulate instead of being overwritten. You can say which node the update counts as coming from, which affects what runs next.
To see every thread a checkpointer holds, for an admin or debugging view, the documentation shows listing checkpoints with no config, and grouping by thread ID yourself, since the call returns one row per checkpoint rather than one per thread.
Editing state is how a reviewer's correction can be applied to a paused run, and the fact that it adds a checkpoint rather than rewriting history is useful for audit, since the original proposal remains visible.
Replay is not free: the side-effect trap
Replay, or time travel, lets you go back to a past checkpoint and run forward again. It is a valuable debugging tool: change a prompt, rewind to the step before the bad decision, and see what would happen. The documentation describes the behaviour precisely. Nodes before the chosen checkpoint are skipped because their results are saved. Nodes after it re-execute, including model calls, API requests and interrupts, and interrupts are always triggered again.
Re-executing means a real tool call runs again. Replay a checkpoint before a node that sends an email, and the email goes out again. Replay before a payment, and you may pay twice. So treat replay on a production thread as a live action unless your tools are safe to repeat. Three habits help:
- Give consequential tools idempotency keys, so a repeat is recognised and ignored by the destination.
- Add a dry-run or sandbox switch to tools, and set it for replays and tests.
- Replay on a copy of the thread, or in a test environment with fake tools, and keep production threads for resuming, not experimenting.
The same logic applies to resuming after an interrupt, where the node restarts from its first line. Our LangGraph approval guide covers that rule in detail.
Choosing where checkpoints live
| Option | Notes from the documentation | Use for |
|---|---|---|
| In-memory saver | Stored in RAM and lost when the process restarts | Experiments and tests |
| SQLite saver | File-based, described as ideal for experimentation and local workflows | Development and small local tools |
| Postgres saver | Described as ideal for production, with async versions | Production on PostgreSQL |
| MongoDB saver | Described as suited to production use | Production on MongoDB |
| Azure Cosmos DB saver | Supports sync and async, with Microsoft Entra ID authentication | Production on Azure |
| Agent Server | Handles persistence for you | If you deploy on LangChain's server |
| Custom checkpointer | Implement the base interface, with a conformance test suite provided | A storage backend not already covered |
Whichever you pick, read the small print. With Postgres, thread IDs live in a length-limited column, so keep them under 255 characters. And if you build your own checkpointer, the documentation stresses that lookup by checkpoint ID must be fast and correct, because time travel and some state reconstruction depend on it.
Protecting what you store
Checkpoints contain whatever is in your agent's state: customer messages, retrieved documents, tool arguments, drafts. They are personal and often confidential data, held in your database, so they need the same care as any other record.
- Encryption: the documentation describes an encrypted serializer that encrypts all persisted state, with the AES key read from an environment variable or supplied directly. You can implement another scheme through the cipher protocol. Manage the key as you would any secret.
- Serialization: the default serializer handles many types, and offers a pickle fallback for objects it cannot encode. Pickle can execute code when loaded, so only use it where you fully trust everything in the store. This is a general property of pickle rather than a point the LangGraph page makes, so assess it for your own threat model.
- Access: restrict who and what can read the checkpoint tables, and avoid putting secrets in agent state at all.
- Retention: the documentation notes that checkpoints accumulate and can increase latency and storage cost, and suggests pruning or a retention policy, for example a scheduled job that deletes old checkpoints. Decide how long a paused run may live, and delete after that.
- Size: for long, append-heavy threads such as conversations, the documentation describes a delta channel that stores increments rather than full values. It is marked beta and needs a recent version, and pruning has special rules with it, so evaluate before adopting.
A checkpoint is not an audit record
It is tempting to treat the checkpoint history as your audit trail, since it records every step. We would not rely on it alone. Checkpoints are designed for resuming and debugging. They can be pruned, they store the agent's working state rather than the business decision, and their shape can change with your graph. An audit record is a separate, append-only log of what was proposed, who approved it, what happened and when.
Keep both, and link them by thread ID and request ID. The checkpointer lets you resume. The audit log lets you answer a regulator, a customer or a colleague. For what that log should contain, see Human Approval Checkpoints for AI Agents in Regulated Finance.
How other approaches compare
LangGraph saves graph state inside the framework. Others reach durability differently, and the differences matter when you choose.
| Approach | How durability works | Point to note |
|---|---|---|
| OpenAI Agents SDK | A paused run can be turned into a serializable RunState that you store in your own database or queue and resume later | The documentation says approval does not guarantee exactly-once execution, recommends idempotency keys, and suggests storing a version marker with pending work |
| Temporal | Workflow history is durable by design, with timers that survive disruption | The AI cookbook says a waiting workflow consumes no compute while it waits |
| Microsoft Agent Framework | Approval state is held in the agent session | The documentation says to use the same session across approval round-trips |
| Amazon Bedrock Agents | Return of control passes data to your application with an invocation ID, and you send results back in the next call | Your application owns persistence between calls |
For a side-by-side of pausing and approval across these, see Human-in-the-Loop AI Agent Frameworks Compared.
Testing recovery before you need it
- Kill the process mid-run with each durability mode you might use, restart, and record what survives. Compare to the documentation.
- Fail one of several parallel nodes and confirm the others are not repeated on resume.
- Crash right after accepting a request, before the first node, and confirm your own admission record shows it arrived.
- Resume the same thread twice and confirm consequential actions happen once.
- Replay a thread in a test environment and list every external call it makes.
- Change your graph definition, then try to resume an old thread, and see what breaks. We found no guidance in the pages we read on migrating saved state between graph versions, so test this yourself and store a version marker with long-lived threads.
- Prune old threads and confirm active ones are untouched.
These tests are cheap, and they turn durability from an assumption into a property you have seen. If you want checkpointing and approval steps designed into an agent for your own workflow, see our agentic workflow orchestration service. For the wider build, see Building Production-Ready AI Agents with LangGraph.
Frequently asked questions
What is a checkpointer in LangGraph?
A checkpointer saves a snapshot of the graph state at each super-step, grouped by thread ID. It enables human-in-the-loop pauses, memory between interactions, time travel for debugging and fault tolerance, because the run can be resumed or replayed from saved state instead of starting again.
Which LangGraph durability mode should I use?
Use sync when losing a step would be costly, such as actions that move money or change records, because every checkpoint is written before the next step starts. Async is faster with a small risk of a missing checkpoint on a crash, and exit saves only when the run ends, so mid-run crashes cannot be recovered. Choose by the cost of repeating or losing work.
What happens if my agent crashes before the first checkpoint?
The LangGraph documentation says the thread is indistinguishable from one that was never invoked, in every durability mode. To tell 'never accepted' from 'accepted but lost', record the request in your own store, for example with an outbox row, before you call the graph.
Is it safe to replay an agent's history?
Replay re-executes every node after the chosen checkpoint, including model calls, API requests and interrupts, so real side effects can happen again. Use idempotency keys, a dry-run mode for tools, and a copy of the thread or a test environment rather than replaying production threads.
Can I use checkpoints as my audit trail?
We would not rely on them alone. Checkpoints exist to resume and debug, can be pruned and reflect working state. Keep a separate append-only audit record of proposals, approvals, outcomes and timestamps, linked to the thread ID.
How do I stop checkpoints growing forever?
The LangGraph documentation recommends pruning old checkpoints or setting a retention policy, for example a scheduled job, and describes a beta delta channel that stores increments for append-heavy threads. Decide how long paused or finished runs should live, delete after that, and test pruning against active threads.
Need help with Agentic AI Development? Talk to the engineers.
Tell us what you want to build. You'll get a straight answer on scope, approach and cost from the people who do the work.
Keep reading
Related guides
Agentic AI
Building Production-Ready AI Agents with LangGraph
The gap between a LangGraph demo and a production agent is almost entirely about state, persistence, and interrupts — not the model doing the reasoning.
11 min read
Agentic AI
Pause an AI Agent for Human Approval with LangGraph interrupt()
How to stop an agent before a risky action, wait as long as a person needs, and resume exactly where it left off. Covers interrupt(), checkpointers, the rules that cause duplicate actions, and approval design.
13 min read
Agentic AI
Human-in-the-Loop AI Agent Frameworks Compared: LangGraph, CrewAI, OpenAI and More
Every serious agent framework can pause for a human decision, but they do it differently. This guide compares how six of them pause, store state, resume and handle risk, and what each leaves for you to build.
14 min read