AI agent state management is the engineering that keeps an agent’s working state alive between executions — so a run interrupted on Tuesday can resume on Wednesday without starting over. The short answer to how agents maintain state and memory across multiple execution sessions is that they do not do it on their own: a model call is stateless, so everything the agent knows must be stored outside the model — as checkpoints keyed to a session, in session stores, or as durable facts — and reloaded when the next session starts. This guide works through each layer: what state actually is, how checkpointing saves a run, where the state lives, how a new session reloads it, and what breaks when it goes wrong.
What ‘state’ means when a session ends
Before choosing how to persist anything, pin down what has to survive. Working state is everything a run needs to continue: the conversation so far, the plan it is halfway through executing, the results of tools it already called, and the work still pending. Durable memory is different: facts, user preferences, and learned procedures that should outlive any single run. The two are stored differently, and confusing them is the most common design mistake in this area.
What is lost when the process dies is anything held only in memory. LangGraph’s InMemorySaver checkpointer, for example, is a dictionary: it disappears when the process dies, which is fine for development and tests but useless for a run that must survive a restart. A session boundary is the hard case: a new process, a new day, a resumed run. The agent starts with nothing, and everything it needs has to come back from storage.
Why a model call cannot remember anything on its own
Each model call sees only the prompt it is given. Without a session or stored state, an agent is stateless and forgets previous messages — the model produces a reply and retains nothing. So persistence is entirely the application’s job: before each run, the stored history must be fetched and put back into the prompt, and after each run, the new items must be saved.
That is what the frameworks’ session mechanisms do. In the OpenAI Agents SDK, passing a session to Runner.run makes the runner fetch the session’s stored conversation items and prepend them to the next turn, then persist the new input and output after the run completes. In LangGraph, the checkpointer restores the saved graph state for the thread when the same thread_id is passed again. Either way, the model itself remembers nothing — the storage layer does the remembering, and the new session reloads rather than replaying the whole conversation from scratch.
Checkpointing: saving a run so it can resume

Checkpointing is the core mechanism for cross-session continuity. LangGraph’s checkpointer saves a snapshot of graph state at every super-step — after each node execution — so a run can be resumed from the last saved step instead of starting over. The snapshot is keyed to a thread: a thread represents one conversation or task, and the thread_id is the persistent cursor. Reusing it resumes the same checkpoint; using a new value starts a brand-new thread with an empty state.
# Fragment: the graph and initial_state are defined earlier in your code.
from langgraph.checkpoint.sqlite import SqliteSaver
checkpointer = SqliteSaver.from_conn_string("checkpoints.db")
app = graph.compile(checkpointer=checkpointer)
# Thread ID scopes state to a specific user + task
config = {"configurable": {"thread_id": "user-123-task-456"}}
result = app.invoke(initial_state, config=config)
# Later, in a new process: resume from where it left off
result = app.invoke(None, config=config) # None resumes from checkpoint
Which checkpointer to use depends on where the agent runs. InMemorySaver is for development and tests. SqliteSaver suits local development and single-instance deployments. PostgresSaver is the production choice, and a Redis checkpointer serves multi-process deployments where many processes share state. The same resume call works in every case: passing None as the input tells LangGraph to pull the state from the checkpointer, so one service can start a task and an entirely different worker service can finish it.
Checkpointing is also what makes pausing possible. LangGraph’s interrupts pause execution at a point in the graph, save the graph state through the persistence layer, and wait indefinitely until the run is resumed with Command(resume=...). The human-in-the-loop flow — pause for approval, resume later, even after a restart — is the same mechanism as crash recovery, just triggered on purpose.
Session stores: where persisted state lives
Checkpoints answer “where does the run’s state live”; session stores answer “where does the conversation live”. The OpenAI Agents SDK provides built-in session memory for exactly this: a session object passed to Runner.run keeps the conversation history for that session, so successive calls share history with no manual stitching between runs.
# Fragment: run inside an async function; the agent and session are
# created once and reused across turns.
from agents import Agent, Runner, SQLiteSession
agent = Agent(name="Assistant", instructions="Reply very concisely.")
session = SQLiteSession("conversation_123")
result = await Runner.run(agent, "What city is the Golden Gate Bridge in?", session=session)
result = await Runner.run(agent, "What state is it in?", session=session)
The backend is a deployment decision with real configuration consequences, not just a setting: local-file storage and a configured production database each imply different setup, and the cloud session needs no database at all — so switching backends means changing how the session’s storage is provisioned. Choose by durability first: an in-memory session holds history only in memory and is lost on restart, while SQLiteSession, RedisSession, and the OpenAI cloud session persist it. Then by sharing: SQLiteSession stores history in a local SQLite file and suits single-process apps, while RedisSession is for horizontally scaled servers where many processes share state. OpenAIConversationsSession stores the history in the OpenAI cloud, so no separate database is needed, and MemorySession is for local development. The SDK also offers SQLAlchemySession for production databases. Latency and cost comparisons between these backends are not verified by the research here, so treat them as deployment questions to measure rather than rankings to assume.
Reloading state into a new session
Rehydration is the resume flow, and it has four steps. First, load the checkpoint: app.get_state(config) reads the current state for a thread without running the graph, which is also how you monitor an agent’s health without interfering with it. Second, restore the state into the new run — in LangGraph, invoking with None as the input resumes from the checkpoint. Third, rebuild the working context: the restored state becomes the run’s starting point, and any durable facts the run needs are loaded from the store. Fourth, verify the restored state before the agent acts on it.
# Fragment: app and config come from the checkpointer example above.
# Retrieve the current state without running the graph
current_checkpoint = app.get_state(config)
print(f"Current State: {current_checkpoint.values}")
# Resume execution from the last saved state
result = app.invoke(None, config)
Two details matter here. What to restore verbatim versus what to summarize: a checkpoint restores the full saved state, which is what you want for resuming work — the plan, the pending work, the results so far. But a long history cannot always be reloaded whole: the older turns may need to arrive as a summary rather than the full record, with the checkpoint carrying the work in progress. That split is a design choice for the session, not something the storage layer decides for you. And verification matters because a checkpoint is only as good as the moment it was written: read it, confirm it is the run you think it is, and only then let the agent act.
Long-term memory across sessions
Checkpoints and durable memory are two different stores with two different jobs, and LangGraph keeps them separate on purpose. A checkpointer manages conversation history within a single session, scoped by thread_id. A store manages facts that persist across all sessions, scoped by user or entity namespace. A new thread_id clears checkpointer context but never touches the store — which is why an agent that should remember a user across conversations needs both, and why passing only a checkpointer is the most common architecture mistake.
# Fragment: an in-process illustration of the Store API.
# InMemoryStore holds data only while the process runs; it does not
# demonstrate persistence after a restart.
from langgraph.store.memory import InMemoryStore
store = InMemoryStore()
# user_id would come from your application context (e.g., session, authentication)
user_id = "user_123"
store.put(
namespace=("user_profiles", user_id),
key="profile",
value={"interests": ["transformers"], "expertise_level": "beginner"},
)
# Retrieve later, in a different session
profile = store.get(namespace=("user_profiles", user_id), key="profile")
The example above uses InMemoryStore, which holds data only while the process runs: it shows the put/get shape of the Store API, not persistence after a restart. A durable session needs a persistent store backend, and a new process that reconnects to the same stored data. Stores hold arbitrary key-value data accessible from any thread, namespaced by a tuple such as ("user_id", "memories"). The rule for what goes where: a fact belongs in long-term memory when it should be recalled in a future session — a preference, a learned procedure, a standing instruction. It belongs in the checkpoint when it is the state of a run in progress — the plan, the pending work, the results so far. Write durable facts back after a session ends, and load them into the next session’s context when the run starts.
What breaks: interruption, failure, and conflicting state

Persistence fails in specific, predictable ways. The first is interruption mid-tool-call. An OpenAI Agents SDK session resumes from the last persisted turn, not the last persisted tool: if the agent dies halfway through a five-tool sequence, the session picks up from the turn boundary. Sessions persist turns, not tool progress, and the SDK has no mid-run checkpointer — if exactly-where-it-crashed resumption is load-bearing, that is a checkpointer’s job.
The second is storage growth. Checkpointing writes roughly one record per step, so a long run accumulates many records; production deployments need a retention or pruning policy, such as a scheduled job deleting checkpoints past a certain age, or storage grows without bound. Separately, an interrupted write can leave a checkpoint incomplete: partial writes are a real failure concern, and the research here does not verify backend-specific atomicity or recovery guarantees, so retention is not a fix for them. The third is side effects: resuming from a checkpoint restores state but does not undo work already dispatched outside the agent. A sent email stays sent; a written record stays written. The resumed run restarts from the last persisted state — it cannot roll back the world.
The fourth is conflicting or stale state. Give each session its own thread_id: the thread is what keeps users and tasks separate. What concurrent writes to the same thread actually do is not verified by the research here, so do not design a system that depends on a particular outcome. And version your state: as you iterate, your state schema changes, and loading an old checkpoint into code with a different schema crashes the agent. Include a version key in your state and a migration strategy or a clear-on-schema-mismatch policy. Finally, do not store large binary blobs in state — they bloat every checkpoint and slow down every transition. Store the blob in external storage and keep only its URI in the state.
AI agent state management in practice: a working pattern for durable sessions
Put together, a durable session is a small loop with no exotic parts. Compile the graph with a durable checkpointer — PostgresSaver in production, SqliteSaver for local work — and give each session its own thread_id. Checkpoints are written automatically after each step, so there is nothing to remember to save. When a new session starts, read the checkpoint with get_state, verify it is the run you expect, and resume with invoke(None, config) — or start fresh with a new thread_id when there is nothing to resume. After the session ends, write durable facts to the store under the user’s namespace, and prune old checkpoints on a schedule.
Three checks keep the pattern honest in production: the state schema carries a version and old checkpoints are migrated or cleared; the retention policy caps checkpoint growth; and the resume path is tested with a real kill-and-restart, not just assumed. When the task is one-shot and nothing needs to survive the run, skip the checkpointer — a fresh session is simpler, and there is nothing to resume. But for any agent that works across days, the rule is simple: the model forgets, the storage remembers, and the session is the thing that ties them together.



