Seeking help with some merge message issues when LangGraph is called in parallel

Can parallel steps modify the same state key?

Yes, if the key is reducer-annotated.
In StateGraph, each key can define a reducer (Value, Value) -> Value to aggregate concurrent updates.
Without a reducer, concurrent writes to the same key in one step raise INVALID_CONCURRENT_GRAPH_UPDATE.

Do they each need separate keys?

Not required, but often cleaner.

You have two valid designns:

  • Shared key + reducer
    Example: messages: Annotated[list[AnyMessage], add_messages]
    Good when branches contribute to one logical stream
  • Separate branch keys + explicit merge node
    Example: weather_msgs, location_msgs, then a downstream merge node combines them.
    Better when you need deterministic branch ownership or clearer debugging

Is unification via join or merge?

Both concepts matter:

  • State merge happens automatically each step via per-key reducers.
  • Join (fan-in) controls execution order: add_edge(["b", "c"], "d") means d runs only after both b and c complete.

reducer handles how values combine
join edge handles when downstream runs

For your “accumulating messages” case
Use add_messages on the messages key. That is the intended reducer for message histories.
Also kep message protocol valid:

  • each AIMessage.tool_calls[i].id should get a matching ToolMessage(tool_call_id=...).

If your tool flow is standard, coonsider using create_agent to avid many manual routing pitfalls