LangGraphLangGraph 1.2 · Python 3.10+
Dashboard
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 →

MessagesState and add_messages

MessagesState is a prebuilt state with one messages key that uses the add_messages reducer, so a returned message is appended to the conversation instead of replacing it.

Last updated: 29 Sep, 2026 · LangGraph 1.2

A conversation must grow one message at a time. That is exactly what a reducer is for, so LangGraph ships one ready-made for messages.

add_messages: a reducer that appends · from the Complete Agentic AI Course In 10 Hours · 180:17 to 185:19

The crash course writes its chatbot's state before any node. It imports Annotated, TypedDict, and add_messages from langgraph.graph.message. add_messages is a reducer: each time the chatbot answers, the new message is appended to the messages list instead of replacing it. The state class is a TypedDict with one key:

python
from typing import Annotated
from typing_extensions import TypedDict
from langgraph.graph.message import add_messages

class State(TypedDict):
    # Messages have the type "list". The `add_messages` function
    # in the annotation defines how this state key should be updated
    # (in this case, it appends messages to the list, rather than overwriting them)
    messages:Annotated[list,add_messages]

Annotated attaches extra information to a type: the key is still a list, and add_messages travels with it as the rule for updating it. MessagesState, below, is that same key, ready-made.

The MessagesState shape

python
from langgraph.graph import MessagesState   # the State above, ready-made

Importing MessagesState

Import MessagesState, the prebuilt state whose messages key already uses the add_messages reducer.

python
from langgraph.graph import MessagesState, StateGraph, START, END
from langchain.messages import HumanMessage, AIMessage

A node that adds a message

Write one node that returns a single AIMessage. Because of the reducer, this message is added to the conversation, not swapped in for it.

python
def reply(state):
    return {"messages": [AIMessage("Hello!")]}   # return one new message

Building the graph

Build the graph on MessagesState with that one node between START and END.

python
builder = StateGraph(MessagesState)
builder.add_node("reply", reply)
builder.add_edge(START, "reply")
builder.add_edge("reply", END)

Running and counting messages

Start the run with one HumanMessage, then print how many messages the state holds and the text of the last one.

python
out = builder.compile().invoke({"messages": [HumanMessage("hi")]})
print(len(out["messages"]), out["messages"][-1].content)   # count, then last text

Appending a message end to end

The same pieces in one file, ready to run.

Example
from langgraph.graph import MessagesState, StateGraph, START, END
from langchain.messages import HumanMessage, AIMessage

def reply(state):
    return {"messages": [AIMessage("Hello!")]}

builder = StateGraph(MessagesState)
builder.add_node("reply", reply)
builder.add_edge(START, "reply")
builder.add_edge("reply", END)

out = builder.compile().invoke({"messages": [HumanMessage("hi")]})
print(len(out["messages"]), out["messages"][-1].content)

Why the state holds two messages

  • The run started with one HumanMessage.
  • The reply node returned one AIMessage, and add_messages appended it, so the state ends with two messages.
  • Without the reducer, the returned list would replace the conversation and the human message would be lost.

Plain list vs add_messages

Plain listadd_messages
A returned message listReplaces the whole listIs appended to it
The conversationLost on each stepGrows across steps
Also doesNothingUpdates a message matched by id, and reads dict form

Where MessagesState fits

  • Any graph that talks to a model uses MessagesState or a state with the add_messages reducer.
  • Subclass MessagesState when you need extra keys alongside the conversation.
Watch out. A messages key with a plain list and no add_messages replaces the conversation on every write. That is the most common memory bug in a chat graph.
Try it yourself
  • Add a second node that appends another AIMessage. How many messages now?
  • Subclass MessagesState with a category key and set it in a node.
PreviousMessages

Every expert started right here.