Skip to main content
State is the shared schema that nodes read and write. Reducers control how each update merges into the current value. This page covers:
  1. How to use state to define a graph’s schema
  2. How to use reducers to control how state updates are processed

Define state

State in LangGraph can be a TypedDict, Pydantic model, or dataclass. The examples below use TypedDict. Prefer that form for most graphs; see Use Pydantic models for graph state when you need runtime validation on inputs. By default, a graph uses the same schema for input and output, and the state determines that schema. See Define input and output schemas for distinct input and output schemas. Start with a messages field. That shape fits many LLM applications. For more detail, see Working with messages in graph state.
This state tracks a list of message objects and an extra integer field. There is no reducer yet, so a node that updates messages must return the full list.

Update state

Build a graph with a single node. The node is a Python function that reads graph state and returns updates. The first argument is always the state:
This node appends a message by concatenating in the return value (no reducer yet) and sets extra_field.
Nodes should return updates to the state directly, instead of mutating the state.
Define a simple graph with this node. Use StateGraph for a graph that operates on this state, then add_node to populate the graph:
LangGraph provides built-in utilities for visualizing a graph. See Visualize your graph for detail.
This graph executes a single node. Invoke it:
Note that:
  • Invocation started by updating a single key of the state.
  • The invocation result includes the entire state.
Inspect message content with pretty-print:

Process state updates with reducers

Each key in the state can have its own reducer function, which controls how updates from nodes are applied. If no reducer is specified, updates to that key replace the previous value. For TypedDict state schemas, define reducers by annotating the field with a reducer function. In the earlier example, the node updated "messages" by returning the full concatenated list. Add a reducer so updates are appended automatically:
The node can then return only the new message:

MessagesState

In practice, lists of messages need more than plain concatenation:
  • Update an existing message in the state by ID.
  • Accept shorthand message formats, such as {"role": "user", "content": "..."}.
LangGraph includes a built-in reducer add_messages that handles these cases:
This shape fits applications that use chat models. For convenience, subclass the prebuilt MessagesState:

Resetting a reducer field

A common source of confusion with reducers is that with a merging reducer, returning an empty value does not clear the field. The reducer merges the right argument into the left one, so an empty update is merged in and previously accumulated values are kept. This pattern matters for error buffers or retry counters that must be cleared between retry attempts:
To clear the field while keeping a merging reducer, wrap the update with Overwrite:
For more information, see Bypass reducers with Overwrite.

Bypass reducers with Overwrite

In some cases, you may want to bypass a reducer and directly overwrite a state value. When a node returns a value wrapped with Overwrite, the reducer is bypassed and the channel is set directly to that value. This is useful when you want to reset or replace accumulated state rather than merge it with existing values. LangGraph provides the Overwrite type for this purpose.
You can also use JSON format with the special key "__overwrite__":
When nodes execute in parallel, only one node can use Overwrite on the same state key in a given super-step. If multiple nodes attempt to overwrite the same key in the same super-step, an InvalidUpdateError will be raised.

Define input and output schemas

By default, StateGraph operates with a single schema, and all nodes communicate using that schema. You can also define distinct input and output schemas. When distinct schemas are specified, an internal schema is still used for communication between nodes. The input schema validates the provided input. The output schema filters the internal data so invoke returns only the fields in the output schema. Define distinct input and output schemas:
Notice that the output of invoke only includes the output schema.

Pass private state between nodes

Nodes sometimes need intermediate data that should not appear in the graph’s main input or output schema. Keep that data private so only the nodes that need it can read it. The following sequential graph has three nodes. Private data passes between the first two steps. The third step only sees the public overall state.

Private channels and streaming

Input, output, and private schemas constrain what each node reads and what invoke returns. They do not hide channels from streaming. Values streaming emits all state channels by default, including private ones. That applies to the values projection from event streaming (stream.values on stream_events / streamEvents with version="v3"), and to graph.stream with stream_mode="values" / streamMode: "values". Values streaming uses the full state, not the output schema. A private channel can be missing from invoke output but visible in the stream. This example uses distinct input, output, overall, and private schemas. invoke returns only the output schema:

View example trace

Open a public LangSmith run for this example.
Streaming with the values projection still includes the private bar channel:

View example trace

Open a public LangSmith run for this example.
To restrict streamed values to specific channels (for example, only the output schema), pass output_keys to graph.stream:
If you only need the channels a node produced in each step (not the full accumulated state), use stream_mode="updates" instead. See Streaming.

Use pydantic models for graph state

A StateGraph accepts a state_schema argument that specifies the shape of the state nodes can access and update. Most examples on this page use a Python-native TypedDict or dataclass for state_schema. state_schema can be any type. Use a Pydantic BaseModel for state_schema when you need runtime validation on inputs.
Known Limitations
  • The output of the graph is not an instance of a Pydantic model.
  • Runtime validation only occurs on inputs to the first node in the graph, not on subsequent nodes or outputs.
  • The validation error trace from Pydantic does not show which node the error arises in.
  • Pydantic’s recursive validation can be slow. For performance-sensitive applications, prefer a dataclass.
Invoke the graph with an invalid input
See below for additional features of Pydantic model state:
When using Pydantic models as state schemas, serialization matters especially when:
  • Passing Pydantic objects as inputs
  • Receiving outputs from the graph
  • Working with nested Pydantic models
The following example shows these behaviors:
Pydantic performs runtime type coercion for certain data types. That can help, but it can also produce unexpected values if you do not account for it.
When working with LangChain message types in a state schema, serialization matters. Use AnyMessage (rather than BaseMessage) for proper serialization and deserialization when message objects travel over the wire.

See also