What is .add_messages()?
add_messages is a LangGraph reducer function — not a method on an object, but a callable you annotate in a state TypedDict to tell LangGraph how to merge state updates for the messages field. When a node returns {"messages": [new_message]}, LangGraph uses the registered reducer to combine the returned value with the existing state. With add_messages as the reducer, incoming messages are appended to the existing list rather than replacing it.
The function signature is add_messages(left: list, right: list | BaseMessage) -> list. The first argument is the current state, the second is the new value from the node's return dict. LangGraph calls this automatically — you do not invoke add_messages() directly in your node functions. Registration happens in the Annotated type hint on the state TypedDict: messages: Annotated[list[BaseMessage], add_messages].
Starting in LangGraph 0.2, add_messages also handles message deduplication via message IDs. If a returned message has the same id as an existing message in state, add_messages updates the existing message in place rather than appending a duplicate. This is how streaming partial message chunks get reassembled: each chunk arrives with the same id, and the reducer progressively replaces the partial message with the latest chunk.
Use Cases
- • Conversation management
- • Message history
- • State updates
- • History composition
- • Message ordering
- • Duplicate handling
Key Features
- ✓ Append messages
- ✓ Ordering
- ✓ Duplicate prevention
- ✓ Simple API
- ✓ State integration
- ✓ History building
When NOT to Use
Direct list append is fine for simple cases.
Notes
Register as reducer, do not call directly in nodes
Register add_messages in the TypedDict annotation: messages: Annotated[list[BaseMessage], add_messages]. In your node function, just return {"messages": [new_msg]} and LangGraph calls the reducer automatically. Calling add_messages(state['messages'], ...) directly inside a node function is redundant and confusing.
Message ID upsert for streaming
LangGraph 0.2+ uses message IDs for upsert behavior. If you return a message with the same id as an existing message, add_messages updates it in place rather than appending. Set the same id on streamed chunks to reassemble partial messages without duplicates.
Clearing history requires RemoveMessage
To clear messages (e.g., reset a conversation), return a RemoveMessage for each message you want to delete, or use the delete_messages helper. Simply returning {"messages": []} is a no-op — add_messages appends an empty list, leaving existing messages intact.
All message types must be JSON-serializable
For LangGraph persistence (checkpointing), all message subclasses must serialize to JSON. Custom BaseMessage subclasses need the type attribute set correctly. Use LangChain's built-in types (HumanMessage, AIMessage, ToolMessage) whenever possible.
Method Signature
state['messages'] = add_messages(state['messages'], new_message)
Parameters
| Parameter | Type | Required | Purpose |
|---|---|---|---|
| messages | List[BaseMessage] | Yes | Existing messages |
Return Value
Type:
List[BaseMessage]
Description:
Updated messages
Example Output:
[message1, message2, ...]
Code Examples
Direct call (non-graph context)
from langgraph.graph import add_messages
state['messages'] = add_messages(
state['messages'],
HumanMessage(content='Hello')
)
Annotated state definition with add_messages reducer
from typing import Annotated
from typing_extensions import TypedDict
from langchain_core.messages import BaseMessage
from langgraph.graph import add_messages, StateGraph, END
class State(TypedDict):
messages: Annotated[list[BaseMessage], add_messages]
def chatbot(state: State):
response = llm.invoke(state["messages"])
return {"messages": [response]}
graph = StateGraph(State)
graph.add_node('chatbot', chatbot)
# add_messages reducer appends response automatically
ID-based upsert for streaming chunk reassembly
from langchain_core.messages import AIMessage
# Streaming: update a partial message by ID
chunk1 = AIMessage(content="Hello", id="msg-1")
chunk2 = AIMessage(content="Hello world", id="msg-1")
state = {"messages": []}
state["messages"] = add_messages(state["messages"], chunk1)
state["messages"] = add_messages(state["messages"], chunk2)
print(len(state["messages"])) # 1 — updated in place, not duplicated
print(state["messages"][0].content) # Hello world
Common Mistakes
❌ Direct append to messages list
✅ Use add_messages() for proper handling
Related LangChain References
Browse the full LangChain API reference index to explore more classes, methods, and decorators, or start with the LangChain introduction tutorial for end-to-end context on building with .add_messages() and the wider framework.
.add_messages() FAQ
What does .add_messages() do in LangChain?
Append messages to conversation history. add_messages is a LangGraph reducer function — not a method on an object, but a callable you annotate in a state TypedDict to tell LangGraph how to merge state updates for the messages field. When a node returns {"messages": [new_message]}, LangGraph uses the registered reducer to combine the returned value with the existing state. With add_messages as the reducer, incoming messages are appended to the existing list rather than replacing it. The function signature is add_mes…
Which LangChain classes support .add_messages()?
.add_messages() is available on Message reducers. Pin your installed LangChain version and verify the method exists in that release before deploying.
When should I use .add_messages()?
Use .add_messages() when your LangChain chains, agents, or pipelines need the behavior described in this guide.
What does .add_messages() return?
.add_messages() returns a List[BaseMessage]. Updated messages
Does .add_messages() have an async equivalent?
.add_messages() does not have a documented async variant. Avoid .add_messages() Direct list append is fine for simple cases.
Where can I explore more LangChain API reference pages?
Open the LangChain API reference index on DevShelfHub to browse classes, methods, and decorators, each with runnable examples, parameters, common mistakes, and cross-links.