langgraphagentsstatecheckpointsai

LangGraph State & Checkpoints: How Agent State Serializes to JSON

·10 min read·AI & JSON

Agents are state machines, and state needs a shape

A single LLM call is stateless. A useful agent is not — it loops, calls tools, waits for a human, and resumes later. LangGraph models that as a graph: nodes do work, edges decide what runs next, and a shared state object flows through. The thing that makes it production-grade is that every transition is checkpointed — serialized and saved — so an agent can pause, survive a crash, and pick up exactly where it left off.

Understanding the JSON underneath that state is what lets you debug agents instead of staring at them.

The typed state schema

State in LangGraph is a typed schema (a TypedDict, dataclass, or Pydantic model) that every node reads and updates. A minimal example:

python
from typing import Annotated, TypedDict
from operator import add

class AgentState(TypedDict):
    messages: Annotated[list, add]   # reducer: new messages append, not overwrite
    step: int
    done: bool

The Annotated[..., add] reducer matters: when a node returns {"messages": [new]}, LangGraph *appends* rather than replaces. Conceptually the state at any checkpoint is just JSON:

json
{
  "messages": [
    { "role": "user", "content": "Book a table for two." },
    { "role": "assistant", "tool_calls": [{ "name": "find_restaurant", "args": { "seats": 2 } }] }
  ],
  "step": 2,
  "done": false
}

Checkpoints: a recovery point, not a log entry

A checkpoint is a snapshot of state after a transition, written through a checkpointer (saver). LangGraph ships several: MemorySaver for development, and SQLite or Postgres savers for production. Because each transition is saved, you get for free what most frameworks bolt on later:

  • Pause / resume — stop for human approval, resume days later from the stored state.
  • Time-travel debugging — replay from any past checkpoint to see how the agent reached a decision.
  • Failure recovery — a crashed or timed-out run restarts from the last good checkpoint instead of the beginning.
  • Horizontal scaling — multiple workers share one checkpoint store, so any instance can continue any thread.

The serializer: JsonPlusSerializer

State often contains things plain JSON can't hold — datetimes, enums, and framework message objects. LangGraph's default JsonPlusSerializer handles this: it serializes with ormsgpack for speed and falls back to an extended JSON format that knows how to encode LangChain/LangGraph types, datetimes, and enums, then reconstruct them on load. So the on-disk form is compact binary, but the mental model — and what you inspect when debugging — is JSON.

Debugging agent state

When an agent misbehaves, the answer is almost always in a checkpoint. A practical loop:

  1. Dump the state at the failing checkpoint and format it — nested message histories are unreadable until pretty-printed.
  2. Diff two consecutive checkpoints to see exactly what a node changed — often a reducer appended where you expected a replace, or a tool wrote the wrong field.
  3. Lock your state shape with a JSON Schema so a node that adds an unexpected key gets caught in review, not in production.
  4. For long message arrays, explore the tree to navigate turns without losing your place.

Production checklist worth internalizing: a typed StateGraph schema, Postgres checkpointing (not MemorySaver), per-node tracing, and recursion limits so a loop can't run away. For related agent-state patterns see Multi-Agent JSON State and AI Agent Memory in JSON.

Frequently asked questions

A checkpoint is a saved snapshot of the graph's state after a transition — a recovery point, not just a log. It enables pause/resume, time-travel debugging, and restarting a failed run from the last good state rather than the beginning.

By the default JsonPlusSerializer, which uses ormsgpack with a fallback to an extended JSON format that can encode datetimes, enums, and LangChain/LangGraph message types and reconstruct them on load.

MemorySaver keeps checkpoints in process memory, so they vanish on restart and can't be shared. A Postgres (or SQLite) checkpointer persists state and lets multiple workers resume the same thread, which is required for reliability and horizontal scaling.

It tells LangGraph how to merge a node's update into state. With add on a list, returned items are appended instead of overwriting the field — which is how message history accumulates across turns.

Try JSON Diff

Diff two agent checkpoints to see exactly what a node changed.