LangGraph Stateful Agents: How Checkpoints and Memory Work
LangGraph is LangChain's framework for building agents as graphs of steps that share a state object. It is stateful because a checkpointer saves that state after each step, so a run can pause and pick up again. A separate Store keeps facts across conversations. The two are easy to confuse. An agent with only a checkpointer forgets a user the moment they open a new chat. By the end you will know which layer to use and how to set it up for production.
What is a LangGraph stateful agent, in one sentence?
A LangGraph stateful agent is a graph of nodes that share one state object, compiled with a checkpointer so each thread's state is saved and can be resumed or rewound.
The LangGraph overview describes the project as "a low-level orchestration framework and runtime" for long-running, stateful agents. It is open source under the MIT license in the LangGraph repository. Version 1.0.0 shipped on October 17, 2025, per PyPI. The latest release as of this update is 1.2.18 from October 26, 2026.
How do checkpointers differ from the Store?
LangGraph has two persistence systems, and the persistence docs treat them as complements.
| Checkpointer | Store | |
|---|---|---|
| Memory type | Short-term, one thread | Long-term, across threads |
| What it saves | The full graph state after each step | Items your code chooses to write |
| Key | A thread_id in the run config | A namespace tuple such as (user_id, "memories") |
| Who writes to it | LangGraph, automatically | Your nodes, explicitly |
| Search | Look up by thread and checkpoint | Key lookup, plus optional semantic search |
| Production backend | PostgresSaver (also MongoDB, Redis, Oracle) | PostgresStore |
Sources: the persistence docs and the memory guide.
How does short-term memory work with threads?
You compile the graph with a checkpointer and pass a thread ID on every call. The memory guide shows the pattern:
checkpointer = InMemorySaver()
graph = builder.compile(checkpointer=checkpointer)
graph.invoke(inputs, {"configurable": {"thread_id": "1"}})
Each call with the same thread ID continues where the last one stopped. The saved checkpoints also let a run wait for a person's approval or replay from an earlier step. After a crash, a run can resume from its last checkpoint. The docs call these features human-in-the-loop, time travel and fault tolerance.
Pick the checkpointer by environment. InMemorySaver lives in RAM and is gone on restart, while SqliteSaver writes to a file that is fine on a laptop. For production the docs point to PostgresSaver, whose setup method creates the tables. The docs add two practical notes. Keep Postgres thread IDs under 255 characters. Plan a pruning or retention policy, because checkpoints pile up over long conversations.
How does the Store give LangGraph long-term memory?
A checkpointer only knows one thread. When a user opens a new chat, the new thread starts empty. The Store carries data across threads. You pass it to compile next to the checkpointer. Nodes then read and write it through the runtime:
memories = await runtime.store.asearch(namespace, query=query, limit=3)
await runtime.store.aput(namespace, str(uuid7()), {"data": "User prefers dark mode"})
Items are grouped under a namespace tuple, usually a user ID plus a label. To search by meaning, give the store an index config with an embedding function and its dimensions. The docs' example uses 1,536 dimensions with OpenAI's text-embedding-3-small. In production, the guide swaps InMemoryStore for PostgresStore.
The Store also answers a subgraph problem. Each subgraph keeps its own checkpoint namespace, so a parent graph may not see a subgraph's updates right away. The persistence docs recommend a Store for data that must cross graph boundaries.
How do you keep a long thread inside the context window?
Persisted state grows. A thread with hundreds of messages will eventually overflow the model's window, and the memory guide gives three tools:
- trim_messages passes only the last N tokens to the model and leaves stored state untouched.
- RemoveMessage deletes messages from state. It needs the add_messages reducer.
- A running summary in state lets you drop older messages once they are summarized. The full example uses a summarization node from LangMem.
Trimming is the safe default. Summarizing saves more tokens, but each summary is a lossy rewrite made by a model.
What does LangGraph leave to you?
LangGraph saves state but has no opinion on what is worth remembering. The Store holds whatever your nodes put there, so someone has to write the logic that extracts a preference or a fact from a conversation. Someone also has to decide when an old memory is wrong. That logic usually means extra model calls per conversation. A wrong extraction stays in the Store until your code removes it.
Deployment is the other open question. On LangSmith's Agent Server, persistence is handled for you. Self-hosting means running Postgres and its migrations yourself.
The bottom line
Use a checkpointer for every LangGraph agent that talks to users. Switch from InMemorySaver to PostgresSaver before production. Add a Store only when something must outlive a thread, such as user preferences. Budget real engineering time for the code that decides what goes into it.
How should coding agents remember across sessions?
LangGraph's split maps onto a problem every developer using coding agents has. A Claude Code session is a thread. It keeps its own context until it ends. The next session starts without the project facts and workflow fixes the last one discovered. Moving to Codex or Cursor starts from zero again.
memU is a separate service that works like a cross-thread store for those hosts. A scheduled background task slices new session logs into jobs. The agent decides what is worth keeping and writes it as memory and skill Markdown, which memU commits. memU itself makes no LLM calls. It indexes what the agent wrote. A standing instruction in CLAUDE.md or AGENTS.md tells each agent to search it before answering, and every supported host reads from the same backend. The design reasoning is in why memU gives memory back to the agent.
To try it with an API key from memu.so, send your agent one message:
Read https://memu.pro/SKILL.md, follow its instructions to install and configure memU
The self-hosted option and supported hosts are listed in the memU GitHub repository.
FAQ
What is a thread_id in LangGraph?
A thread_id is the key a checkpointer uses to save and load one conversation's state. You pass it in the run config, and every call with the same ID continues that thread.
What is the difference between MemorySaver and InMemoryStore?
MemorySaver (InMemorySaver) is a checkpointer that saves one thread's graph state in RAM. InMemoryStore holds long-term items across threads. Both lose their data when the process restarts.
Does LangGraph have long-term memory?
Yes, through the Store. Your nodes write items under a namespace such as a user ID. Any thread can later read or semantically search them.
Which checkpointer should I use in production?
The LangGraph docs recommend a database-backed checkpointer, with PostgresSaver as the main example. MongoDB, Redis and Oracle checkpointers also exist.
Is LangGraph free?
The LangGraph library is open source under the MIT license. LangSmith Deployment, the hosted platform for running LangGraph agents, is a separate product.