LangGraphLangGraph 1.2 · Python 3.10+
0%
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
38 small wins to finish your pathNext lesson →

Reducers: combining values in the state

A reducer decides how a node's returned value for a key is combined with the value already in the state. By default a returned key overwrites the old value; a reducer can append instead.

Last updated: 27 Sep, 2026 · LangGraph 1.2

Overwriting is fine for a category or a reply. It is wrong for a running list, such as notes gathered by several steps, where each step should add to the list, not replace it.

How you declare a reducer

python
from typing_extensions import TypedDict, Annotated
import operator

class State(TypedDict):
    plain: int                              # no reducer: overwrite
    acc: Annotated[list[int], operator.add] # reducer: append (list + list)

The state

Start with the state. plain is a normal key. acc is wrapped in Annotated with operator.add, the reducer that appends to a list instead of overwriting it.

python
from typing_extensions import TypedDict, Annotated
import operator
from langgraph.graph import StateGraph, START, END

class State(TypedDict):
    plain: int                              # no reducer: each write overwrites
    acc: Annotated[list[int], operator.add] # reducer: each write is appended

The two writing nodes

Add two nodes that run one after the other. Each writes to both keys, so both keys are written twice during the run.

python
builder = StateGraph(State)
builder.add_node("n1", lambda s: {"plain": 1, "acc": [1]})  # writes 1 to both keys
builder.add_node("n2", lambda s: {"plain": 2, "acc": [2]})  # writes 2 to both keys
builder.add_edge(START, "n1")
builder.add_edge("n1", "n2")
builder.add_edge("n2", END)

Running the graph

Compile and run once, starting from an empty list, and print the final state.

python
print(builder.compile().invoke({"plain": 0, "acc": []}))

The reducer in a full run

The same pieces in one file, ready to run.

Example
from typing_extensions import TypedDict, Annotated
import operator
from langgraph.graph import StateGraph, START, END

class State(TypedDict):
    plain: int                              # no reducer: overwrite
    acc: Annotated[list[int], operator.add] # reducer: append (list + list)

builder = StateGraph(State)
builder.add_node("n1", lambda s: {"plain": 1, "acc": [1]})
builder.add_node("n2", lambda s: {"plain": 2, "acc": [2]})
builder.add_edge(START, "n1")
builder.add_edge("n1", "n2")
builder.add_edge("n2", END)

print(builder.compile().invoke({"plain": 0, "acc": []}))

What each key ended as

  • plain has no reducer, so each write overwrites: it ends at 2, the last value written.
  • acc is annotated with operator.add, so the two writes combine: [1] + [2] becomes [1, 2].
  • A reducer is a small function that takes the old value and the new one and returns the combined value.

Default vs reducer

KeyBehaviorEnds as
plain (no reducer)Overwrite with the latest write2
acc (operator.add)Append each write[1, 2]

When you reach for a reducer

  • Collecting results from steps that run in parallel (each adds to a list).
  • A conversation's messages, which must append, not replace, one message at a time.
  • Any running total or log the graph builds up across steps.
Watch out. If two nodes finish in the same step and both write a key that has no reducer, LangGraph refuses the run: a key can take only one value per step. Add a reducer so the two writes combine.
Try it yourself
  • Remove the Annotated[...] on acc and run again. What is acc now?
  • Change the reducer to keep only the largest value instead of appending.
  • Add a third node that writes acc: [3] and predict the final list.

Slow is fine. Stopping is the only problem.