Why do LangGraph state fields need reducers?
Same topic, related formats. Practice these next.
Same topic, related formats. Practice these next.
Any LangGraph state field that should accumulate or that parallel nodes might write in one superstep needs a reducer; without one, last write wins overwrite silently drops history.
Picture a shared whiteboard during a brainstorming meeting. By default, when someone walks up and writes on a section, they wipe whatever was there and write their own line. That works for a status field that just shows the latest temperature. It fails badly for the section where everyone is supposed to add an idea. You would end up with only the last person's idea on the board. The fix is a rule per section that says 'add to this section, do not replace it.' That rule is the reducer.
Everything you need to truly understand this topic: intuition, mechanics, step by step explanation, code, formulas, and worked example. Click to expand.
Everything you need to truly understand this topic: intuition, mechanics, step by step explanation, code, formulas, and worked example.
Everything important, quickly.
5 minutes: the default overwrite rule, the Annotated reducer syntax, what add_messages actually does, and why parallel branches break without reducers.
from typing import Annotated
from typing_extensions import TypedDict
from langgraph.graph.message import add_messages
from langchain_core.messages import BaseMessage
import operator
class State(TypedDict):
# Chat history: needs append-semantics, de-duplication by ID
messages: Annotated[list[BaseMessage], add_messages]
# Retrieved docs accumulated across parallel retriever nodes
docs: Annotated[list[dict], operator.add]
# Latest value wins: no reducer needed
current_step: str
# Custom reducer for merging partial worker outputs
worker_results: Annotated[dict, lambda a, b: {**a, **b}]| Field shape | Reducer | Why |
|---|---|---|
| Chat history | `add_messages` | Append + de-dup by message ID |
| Accumulating list | `operator.add` | Concatenate lists across nodes |
| Merging dicts | Custom lambda or function | Spread-merge keys from parallel writers |
| Latest status / flag | No reducer (overwrite) | Last write reflects current truth |
| Counter | Custom `lambda a, b: a + b` | Sum increments from parallel branches |
Real products, models, and research that use this idea.
What an interviewer would ask next. Try answering before peeking at the approach.
Red flags and common mistakes that signal junior thinking. Click to expand.
Declaring `messages: list[BaseMessage]` without `Annotated[..., add_messages]` and then debugging why chat history disappears after every node return.
The night-before-the-interview bullets. Scan these on the way to the call.
Primary sources. Skim if you want the original framing.