I was wiring up a LangGraph agent today — parent graph calling a subgraph — and hit a wall trying to understand how data flows between them.
The docs say subgraphs work via shared channel names. That phrase sat there, doing nothing for me, until I actually dug in.
Here's what it means.
Channels are just your state keys
In LangGraph, your state is a TypedDict. Every key in that dict is called a channel.
class ParentState(TypedDict):
messages: list # channel: "messages"
user_id: str # channel: "user_id"
That's it. No magic. A channel is a named slot in your state.
The "shared" part is where it clicks
When your subgraph has a key with the same name as the parent, LangGraph automatically maps data in and out. No explicit wiring needed.
class ParentState(TypedDict):
messages: list # shared
user_id: str # shared
final_output: str
class SubgraphState(TypedDict):
messages: list # ✅ same name — gets auto-mapped
user_id: str # ✅ same name — gets auto-mapped
temp_data: str # only lives inside the subgraph
When the parent invokes the subgraph, messages and user_id flow in automatically. When the subgraph finishes, those same keys flow back out and update the parent state.
temp_data? It stays inside the subgraph and gets dropped when it exits.
A quick mental model
Think of it like function scope, but for graph state:
![]()
The parent never knew about temp_data. The subgraph never knew about final_output. Only the overlap gets synced.
Why this matters in practice
This means your subgraph is reusable. It doesn't need to know the full shape of every parent graph that might call it. As long as the keys it needs exist in the parent under the same name, it just works.
It also means you can use the subgraph as a clean isolation boundary — internal working state stays internal, and only the agreed-upon keys get surfaced back up.
Once I understood this, the subgraph wiring made complete sense. The name matching is the contract between parent and child.