State Management (in LangGraph)

State is the heart of LangGraph. Nodes don't call each other or pass arguments around — they all read from and write to one shared state object. Understanding how state is defined, updated, and persisted is what separates a graph that works from one that mysteriously loses data or overwrites itself.

💡 In one line: LangGraph state is a shared, typed object that every node reads and updates — with reducers deciding how updates merge.

What is State?

State is a typed, shared object that flows through the graph. Each node receives the current state and returns a partial update; LangGraph merges that update into the state and passes it on. Nodes are decoupled — they communicate only through state.

Defining State

Usually a TypedDict (or a Pydantic model / dataclass):


The schema defines what the graph carries.

Nodes Return Partial Updates

A node returns only the keys it changes — not the whole state:


This is the most common source of confusion: return partial updates, not a full copy.

Reducers (Critical)

A reducer decides how an update merges into the existing value:

  • Default — overwrite the old value.
  • Annotated[list, add] — append (the classic for messages).
  • Custom — any merge function you define.

Get this wrong and nodes silently overwrite each other — the classic LangGraph bug.

The Update Cycle

Whiteboard
Whiteboard diagram


MessagesState

For chat agents, LangGraph ships a prebuilt MessagesState with a messages key and the append reducer already configured — the standard base for conversational graphs.

Persistence: Checkpointers

A checkpointer saves the state at every step, keyed by thread_id:

  • InMemorySaver — development.
  • SqliteSaver — local persistence.
  • PostgresSaver — production.

This gives you durability (survive a restart), resumability, human-in-the-loop pauses, and time travel.


Threads vs. Long-Term Store

  • Checkpointer (thread_id) — state for one conversation.
  • Store (namespace) — facts across all conversations for a user.

Keep thread_id and user_id distinct — mixing them up is the classic production mistake.

Managing State Size

Message lists grow unbounded, and long contexts degrade quality. Fix with trimming (trim_messages), summarising older turns, or keeping large artefacts out of state (store a reference, not the whole file).

Time Travel

Because every step is checkpointed, you can replay a past run or branch an alternate history from any step — a genuinely powerful debugging tool.

Best Practices

  • Keep the state schema minimal and typed.
  • Use reducers deliberately — especially append for messages.
  • Return partial updates from nodes.
  • Use a durable checkpointer in production; trim or summarise long histories.

Summary

  • State is a shared, typed object; nodes communicate only through it.
  • Nodes return partial updates, and reducers decide overwrite vs. append.
  • MessagesState is the prebuilt base for chat agents.
  • Checkpointers persist state per thread_id — enabling durability, HITL, and time travel.
  • Keep state small: trim, summarise, and store references rather than blobs.Â