Skip to content

[Streaming] Cannot correlate subgraph namespace with parent tool_call_id when streaming with subgraphs=True #6714

@GeekRicardo

Description

@GeekRicardo

Checked other resources

  • This is a bug, not a usage question. For questions, please use the LangChain Forum (https://forum.langchain.com/).
  • I added a clear and detailed title that summarizes the issue.
  • I read what a minimal reproducible example is (https://stackoverflow.com/help/minimal-reproducible-example).
  • I included a self-contained, minimal example that demonstrates the issue INCLUDING all the relevant imports. The code run AS IS to reproduce the issue.

Example Code

from typing import Annotated, TypedDict
from langchain_core.messages import AIMessage, HumanMessage
from langchain_core.tools import tool
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langgraph.prebuilt import ToolNode


class State(TypedDict):
    messages: Annotated[list, add_messages]


# Create two simple subgraphs
def create_subgraph(name: str):
    def node(state: State):
        return {"messages": [AIMessage(content=f"Response from {name}")]}
    
    builder = StateGraph(State)
    builder.add_node("respond", node)
    builder.add_edge(START, "respond")
    builder.add_edge("respond", END)
    return builder.compile()


subgraph_a = create_subgraph("subgraph_a")
subgraph_b = create_subgraph("subgraph_b")


@tool
def call_subgraph_a(query: str) -> str:
    """Call subgraph A"""
    result = subgraph_a.invoke({"messages": [HumanMessage(content=query)]})
    return result["messages"][-1].content


@tool
def call_subgraph_b(query: str) -> str:
    """Call subgraph B"""
    result = subgraph_b.invoke({"messages": [HumanMessage(content=query)]})
    return result["messages"][-1].content


def model_node(state: State):
    # Model returns two parallel tool calls
    return {
        "messages": [
            AIMessage(
                content="",
                tool_calls=[
                    {"id": "tool_call_A", "name": "call_subgraph_a", "args": {"query": "hello"}},
                    {"id": "tool_call_B", "name": "call_subgraph_b", "args": {"query": "world"}},
                ],
            )
        ]
    }


builder = StateGraph(State)
builder.add_node("model", model_node)
builder.add_node("tools", ToolNode([call_subgraph_a, call_subgraph_b]))
builder.add_edge(START, "model")
builder.add_edge("model", "tools")
builder.add_edge("tools", END)

graph = builder.compile()


async def main():
    async for chunk in graph.astream(
        {"messages": [HumanMessage(content="Call both subgraphs")]},
        stream_mode=["updates", "messages"],
        subgraphs=True,
    ):
        namespace, event_type, payload = chunk

        # Try to extract tool_call_id from payload
        tool_call_id = "?"
        if event_type == "messages" and isinstance(payload, tuple):
            msg = payload[0]
            if hasattr(msg, 'tool_call_id'):
                tool_call_id = msg.tool_call_id

        print(f"namespace: {namespace}, tool_call_id: {tool_call_id}")


import asyncio
asyncio.run(main())

----------
Output:

namespace: ()
event_type: messages
tool_call_id: ?
---
namespace: ()
event_type: updates
tool_call_id: ?
---
namespace: ('tools:1032c8d0-64e7-7979-524c-31671560f76a',)
event_type: messages
tool_call_id: ?
---
namespace: ('tools:1032c8d0-64e7-7979-524c-31671560f76a', '1')
event_type: messages
tool_call_id: ?
---
namespace: ('tools:1032c8d0-64e7-7979-524c-31671560f76a',)
event_type: updates
tool_call_id: ?
---
namespace: ('tools:1032c8d0-64e7-7979-524c-31671560f76a', '1')
event_type: updates
tool_call_id: ?
---
namespace: ()
event_type: messages
tool_call_id: tool_call_A
---
namespace: ()
event_type: messages
tool_call_id: tool_call_B
---
namespace: ()
event_type: updates
tool_call_id: ?
---

Error Message and Stack Trace (if applicable)

Description

Problem:

When streaming from a graph with subgraphs=True, there is no way to correlate the subgraph's namespace with the original tool_call_id from the parent graph.

Timeline of the issue:

Stage namespace tool_call_id Issue
Subgraph streaming ('tools:uuid',) ❌ Unknown Need correlation but cannot get it
Subgraph streaming ('tools:uuid', '1') ❌ Unknown Need correlation but cannot get it
ToolMessage returned () tool_call_A Too late, streaming already ended
ToolMessage returned () tool_call_B Too late, streaming already ended

Why this matters:

In a streaming UI, when displaying parallel tool executions, we need to show each subgraph's streaming output under its corresponding tool call:

┌─────────────────────────────────┐
│ [tool_call_A] call_subgraph_a   │
│   └─ streaming...               │  ← Which namespace does this belong to?
├─────────────────────────────────┤
│ [tool_call_B] call_subgraph_b   │
│   └─ streaming...               │  ← Which namespace does this belong to?
└─────────────────────────────────┘

When we receive a chunk with namespace: ('tools:uuid', '1'), we cannot determine whether it belongs to tool_call_A or tool_call_B.

Expected behavior:

One of the following:

  1. Include tool_call_id in the streaming chunk metadata
  2. Provide a mapping from namespace to tool_call_id accessible during streaming
  3. Use tool_call_id as part of the namespace (e.g., ('tools:tool_call_A',) instead of ('tools:uuid',))

System Info

System Information

OS: MacOS M4
OS Version: MacOS 15.7.3
Python Version: 3.13.9

Package Information

langgraph: 1.0.5
langchain-core: 1.2.7

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingpendingawaiting review/confirmation by maintainer

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions