## TL;DR
This error means one graph step tried to write two separate values to the `messages` key, and that key has no reducer to combine them. Add `add_messages` as the reducer on the messages field of your state, or restructure the step so it emits a single update. The fix is usually one line in the state schema.

## Error
```text
InvalidUpdateError: At key 'messages': Can receive only one value per step
```

## Steps

1. Find which node runs in the failing step. Stream the graph in debug mode and watch the node names:
```python
for chunk in graph.stream(inputs, stream_mode="debug"):
    print(chunk)
```
Expected output: the debug stream shows the node name for the step that raises. That node is writing to `messages` twice, or two parallel nodes are both writing to it in the same step.

2. Check the state schema for a reducer on `messages`:
```python
from typing import Annotated
from typing_extensions import TypedDict
from langgraph.graph.message import add_messages

class State(TypedDict):
    messages: Annotated[list, add_messages]
```
Expected output: with the reducer present, parallel writes merge instead of colliding. If your schema is a plain `TypedDict` with `messages: list` and no `Annotated`, that missing reducer is the bug.

3. If two parallel branches both return messages, have each return one list and let the reducer merge them:
```python
def branch_a(state):
    return {"messages": [AIMessage(content="a")]}

def branch_b(state):
    return {"messages": [AIMessage(content="b")]}
```
Expected output: both messages land in state, no InvalidUpdateError. Without the reducer, the second write in the same step is what raises.

4. If the step uses `Send()` for fan-out, return the Send objects from a single node and keep the state update in that one return:
```python
def router(state):
    return [Send("worker", {"text": state["text"]}) for _ in range(3)]
```
Expected output: fan-out runs without touching the parent step's `messages` key, so there is no second write to collide with.

5. Re-run the graph and confirm the step completes and the final state holds the merged messages.
Expected output: the stream finishes, and the last state shows messages from every branch in order.

## When to use
- The traceback names a key like `messages` and says only one value per step is allowed
- The error appears right after adding a parallel branch or a new node
- A node returns a dict that writes the same key twice, or two nodes share one step

## When not to use
- The error names a different exception type (read the actual message first)
- The graph fails at compile time (that is a wiring problem, not a step-write problem)
- State updates succeed but values look wrong (that is reducer logic, not this error)

## Variant phrasings

### InvalidUpdateError at key 'count' or another custom key
Same cause on a non-message channel. Add a reducer for that key (operator.add works for numbers) or write it once per step.

### error only appears with parallel nodes, fine in serial
Parallel nodes share a step. In serial runs each node gets its own step, so the collision never happens. The reducer is still the fix.

## Why it happens
LangGraph channels accept one write per step unless a reducer is attached. The `messages` key is special because nearly every node writes to it, so the moment two nodes share a step (parallel branches, or a node that returns two updates for the same key), the channel sees two values and raises. `add_messages` exists exactly to merge those writes.

## Edge cases
- Returning `{"messages": msg}` with a single message object instead of a list can also collide; always return a list.
- A node that calls `Send()` AND returns a messages update in the same step writes twice; split them into separate nodes.
- Custom reducers must be pure functions; a reducer with side effects produces nondeterministic merged state.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_rhxU3Z7c93WFQ9B28ZX1tQ
