RemoteGraph.astream(subgraphs=True) emits incorrect namespaces vs in-memory Pregel graphs #1098

Open
opened 2026-02-20 17:43:03 -05:00 by yindo · 0 comments
Owner

Originally created by @Rakshit-gen on GitHub (Dec 17, 2025).

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

import asyncio
import re

from langgraph.graph import StateGraph, START, END
from langgraph.pregel import Pregel
from langgraph.pregel.remote import RemoteGraph
from langgraph.types import RunnableConfig

# Dummy node
def echo_node(state: dict) -> dict:
    return state

# Build a simple in-memory graph
builder = StateGraph(dict)
builder.add_node("echo", echo_node)
builder.add_edge(START, "echo")
builder.add_edge("echo", END)
inmem_graph: Pregel = builder.compile()

# Assume we have deployed the same graph remotely and know its assistant_id/URL.
# For the minimal repro, you can plug in your own values here.
remote_graph = RemoteGraph(
    "my_assistant_id",
    url="https://my-langgraph-server",
)

uid_pattern = re.compile(
    r"[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}"
)

def monotonic_uid():
    val = 0
    while True:
        val += 1
        hexval = f"{val:032x}"
        yield f"{hexval[:8]}-{hexval[8:12]}-{hexval[12:16]}-{hexval[16:20]}-{hexval[20:32]}"

async def main():
    config: RunnableConfig = {"configurable": {"thread_id": "thread_1"}}

    # Collect namespaces from RemoteGraph
    remote_namespaces = []
    uid_gen = monotonic_uid()
    async for ns, messages in remote_graph.astream(
        {"messages": [{"role": "user", "content": "hello"}]},
        config,
        stream_mode="messages",
        subgraphs=True,
    ):
        # Normalize UUIDs for comparison
        ns = tuple(uid_pattern.sub(next(uid_gen), p) for p in ns)
        remote_namespaces.append(ns)

    # Collect namespaces from in-memory graph
    inmem_namespaces = []
    uid_gen = monotonic_uid()
    async for ns, messages in inmem_graph.astream(
        {"messages": [{"role": "user", "content": "hello"}]},
        config,
        stream_mode="messages",
        subgraphs=True,
    ):
        ns = tuple(uid_pattern.sub(next(uid_gen), p) for p in ns)
        inmem_namespaces.append(ns)

    print("Remote namespaces:", remote_namespaces)
    print("In-memory namespaces:", inmem_namespaces)

asyncio.run(main())

Error Message and Stack Trace (if applicable)

# TODO: Fix the namespace matching in the next api release.
# assert namespaces == inmem_namespaces

Description

I’m trying to use the langgraph library to stream from a remote graph using RemoteGraph.astream(..., subgraphs=True) and compare/debug its behavior against an equivalent in-memory Pregel graph.
I expect the namespaces emitted when subgraphs=True to match the in-memory graph semantics: caller namespace (when RemoteGraph is used as a subgraph) plus the remote graph’s internal namespace hierarchy, so that traces and tests like test_remote_graph_stream_messages_tuple can assert namespaces == inmem_namespaces.
Instead, RemoteGraph.astream parses mode/ns from chunk.event, then correctly prefixes ns with the caller namespace, but later re-parses mode/ns again from chunk.event inside the if subgraphs: block, overwriting the caller-prefixed ns. This causes the final namespaces yielded to differ from in-memory Pregel behavior and breaks namespace-based comparisons and observability for remote subgraphs.

System Info

python -m langchain_core.sys_info

or, if you use python3

python3 -m langchain_core.sys_info

Originally created by @Rakshit-gen on GitHub (Dec 17, 2025). ### Checked other resources - [x] This is a bug, not a usage question. For questions, please use the LangChain Forum (https://forum.langchain.com/). - [x] I added a clear and detailed title that summarizes the issue. - [x] I read what a minimal reproducible example is (https://stackoverflow.com/help/minimal-reproducible-example). - [x] 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 ```python import asyncio import re from langgraph.graph import StateGraph, START, END from langgraph.pregel import Pregel from langgraph.pregel.remote import RemoteGraph from langgraph.types import RunnableConfig # Dummy node def echo_node(state: dict) -> dict: return state # Build a simple in-memory graph builder = StateGraph(dict) builder.add_node("echo", echo_node) builder.add_edge(START, "echo") builder.add_edge("echo", END) inmem_graph: Pregel = builder.compile() # Assume we have deployed the same graph remotely and know its assistant_id/URL. # For the minimal repro, you can plug in your own values here. remote_graph = RemoteGraph( "my_assistant_id", url="https://my-langgraph-server", ) uid_pattern = re.compile( r"[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}" ) def monotonic_uid(): val = 0 while True: val += 1 hexval = f"{val:032x}" yield f"{hexval[:8]}-{hexval[8:12]}-{hexval[12:16]}-{hexval[16:20]}-{hexval[20:32]}" async def main(): config: RunnableConfig = {"configurable": {"thread_id": "thread_1"}} # Collect namespaces from RemoteGraph remote_namespaces = [] uid_gen = monotonic_uid() async for ns, messages in remote_graph.astream( {"messages": [{"role": "user", "content": "hello"}]}, config, stream_mode="messages", subgraphs=True, ): # Normalize UUIDs for comparison ns = tuple(uid_pattern.sub(next(uid_gen), p) for p in ns) remote_namespaces.append(ns) # Collect namespaces from in-memory graph inmem_namespaces = [] uid_gen = monotonic_uid() async for ns, messages in inmem_graph.astream( {"messages": [{"role": "user", "content": "hello"}]}, config, stream_mode="messages", subgraphs=True, ): ns = tuple(uid_pattern.sub(next(uid_gen), p) for p in ns) inmem_namespaces.append(ns) print("Remote namespaces:", remote_namespaces) print("In-memory namespaces:", inmem_namespaces) asyncio.run(main()) ``` ### Error Message and Stack Trace (if applicable) ```shell # TODO: Fix the namespace matching in the next api release. # assert namespaces == inmem_namespaces ``` ### Description I’m trying to use the langgraph library to stream from a remote graph using RemoteGraph.astream(..., subgraphs=True) and compare/debug its behavior against an equivalent in-memory Pregel graph. I expect the namespaces emitted when subgraphs=True to match the in-memory graph semantics: caller namespace (when RemoteGraph is used as a subgraph) plus the remote graph’s internal namespace hierarchy, so that traces and tests like test_remote_graph_stream_messages_tuple can assert namespaces == inmem_namespaces. Instead, RemoteGraph.astream parses mode/ns from chunk.event, then correctly prefixes ns with the caller namespace, but later re-parses mode/ns again from chunk.event inside the if subgraphs: block, overwriting the caller-prefixed ns. This causes the final namespaces yielded to differ from in-memory Pregel behavior and breaks namespace-based comparisons and observability for remote subgraphs. ### System Info python -m langchain_core.sys_info # or, if you use python3 python3 -m langchain_core.sys_info
yindo added the bugpending labels 2026-02-20 17:43:03 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: langchain-ai/langgraph#1098