langchain - ✅(Solved) Fix Nested subgraph rendering fails for 3+ levels with `draw_mermaid` and `xray=True` [6 pull requests, 4 comments, 3 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
langchain-ai/langchain#35492Fetched 2026-04-08 00:25:57
View on GitHub
Comments
4
Participants
3
Timeline
19
Reactions
0
Timeline (top)
cross-referenced ×6commented ×4labeled ×3referenced ×3

When visualizing a LangGraph with 3 or more levels of nested subgraphs using get_graph(xray=True).draw_mermaid(), the Mermaid output fails to create proper subgraph boxes for the nested hierarchy. Instead, nodes appear as flat elements with escaped colon characters in their IDs.

Error Message

Error Message and Stack Trace (if applicable)

No error is raised. The issue is a rendering defect — the generated Mermaid diagram fails to properly render nested subgraph boxes for 3 or more levels of nesting.

Root Cause

The Graph.extend() method creates hierarchical node IDs by joining names with colons (e.g., parent:child:grandchild).

The draw_mermaid() function in langchain_core/runnables/graph_mermaid.py has two code paths for subgraph rendering:

  1. add_subgraph() function (recursive) - Handles subgraphs that contain internal edges between their nodes
  2. "Empty subgraphs" section (around line 239) - Handles subgraphs where all nodes connect to nodes outside the subgraph (no internal edges)

The bug exists in the "empty subgraphs" section. The original code used this condition:

if ":" not in prefix:
    # ... create subgraph box

This condition completely skipped multi-level prefixes like parent:child. Since each graph wraps its child as a single subgraph node, the nested subgraph's nodes have no internal edges (they connect only to parent nodes), which forces the code into the "empty subgraphs" path. With ":" not in prefix being false for parent:child, the subgraph boxes never get created in the Mermaid output.

As a result, 3+ level nested subgraphs don't render their subgraph containers, causing the nodes to appear flat with escaped colons in their IDs (parent\3achild\3agrandchild).

Fix Action

Fix / Workaround

  • This is a bug, not a usage question.
  • I added a clear and descriptive title that summarizes this issue.
  • I used the GitHub search to find a similar question and didn't find it.
  • I am sure that this is a bug in LangChain rather than my code.
  • The bug is not resolved by updating to the latest stable version of LangChain (or the specific integration package).
  • This is not related to the langchain-community package.
  • I posted a self-contained, minimal, reproducible example. A maintainer can copy it and run it AS IS.

Other Dependencies

httpx: 0.28.1 jsonpatch: 1.33 orjson: 3.11.7 packaging: 26.0 pydantic: 2.12.5 pyyaml: 6.0.3 requests: 2.32.5 requests-toolbelt: 1.0.0 tenacity: 9.1.4 typing-extensions: 4.15.0 uuid-utils: 0.14.1 xxhash: 3.6.0 zstandard: 0.25.0

PR fix notes

PR #35493: fix(core): fix nested subgraph rendering for 3+ level nesting in draw_mermaid

Description (problem / solution / changelog)

Fixes #35492

The "empty subgraphs" section in draw_mermaid() skips multi-level prefixes (e.g., parent:child) due to the if ":" not in prefix check, causing 3+ level nested subgraph boxes to not render at all. This fix splits the prefix on : and opens subgraph blocks for each nesting level.

Before: if ":" not in prefix → skips multi-level prefixes entirely After: prefix.split(":") → iterates each level, opens subgraph blocks per level

How did you verify your code works?

  • All 22 tests in test_graph.py pass (18 snapshots passed) (17 snapshots passed, no updates needed)
  • Added test_nested_empty_subgraph_mermaid regression test
  • make format and make lint pass
  • Manually verified correct rendering for 3-level, 4-level nesting with draw_mermaid_png()

Screenshots

Before (bug): <img width="273" height="234" alt="image" src="https://github.com/user-attachments/assets/a00aed75-143e-4bb0-a7d7-cc0053412605" />

After (fix): <img width="230" height="334" alt="image" src="https://github.com/user-attachments/assets/fb1223fe-397f-4bb2-844b-b56e08b2a439" />

Changed files

  • libs/core/langchain_core/runnables/graph_mermaid.py (modified, +15/-6)
  • libs/core/tests/unit_tests/runnables/__snapshots__/test_graph.ambr (modified, +23/-0)
  • libs/core/tests/unit_tests/runnables/test_graph.py (modified, +33/-0)

PR #35500: fix(core): render nested subgraph boxes for 3+ levels in draw_mermaid

Description (problem / solution / changelog)

Summary

  • Fixes draw_mermaid() with xray=True failing to render nested subgraph boxes for 3+ levels of nesting
  • The "empty subgraphs" code path skipped multi-level prefixes (if ":" not in prefix), causing deeply nested nodes to appear as flat elements with escaped colons instead of properly nested subgraph containers
  • Additionally, add_subgraph() only discovered nested subgraphs via edge_groups, missing empty subgraphs entirely

Changes

  • Precompute all_subgraph_prefixes — expands subgraph_nodes keys into all ancestor prefixes so the recursive functions can discover intermediate nesting levels
  • Add add_empty_subgraph() — recursive function that opens a subgraph block, renders its nodes, recurses into child subgraphs (both edge-based and empty), and closes
  • Extend add_subgraph() — after the existing edge-groups recursion, also discover nested empty subgraphs from all_subgraph_prefixes
  • Replace flat empty-subgraph loop — the old if ":" not in prefix loop is replaced with a root-level scan that calls add_empty_subgraph() recursively

Test plan

  • Added test_empty_nested_subgraph_mermaid — reproduces the exact bug (3-level nesting with no internal edges)
  • All 15 existing mermaid tests pass unchanged (snapshots verified)
  • New snapshot shows correct nested subgraph parentsubgraph childgrandchild output

Closes #35492

🤖 Generated with Claude Code

Changed files

  • libs/core/langchain_core/runnables/graph_mermaid.py (modified, +58/-11)
  • libs/core/tests/unit_tests/runnables/__snapshots__/test_graph.ambr (modified, +23/-0)
  • libs/core/tests/unit_tests/runnables/test_graph.py (modified, +44/-0)

PR #35501: Fix nested subgraph rendering for 3+ levels in draw_mermaid with xray=True

Description (problem / solution / changelog)

Addresses #35492

Changed files

  • libs/core/langchain_core/runnables/graph_mermaid.py (modified, +37/-11)

PR #35515: Fix Mermaid rendering for nested empty subgraphs

Description (problem / solution / changelog)

Summary

  • fix Mermaid subgraph traversal to include nested prefixes even when they have no internal edges
  • preserve deterministic prefix ordering while expanding missing ancestor prefixes
  • add regression coverage for parent:child:grandchild nested-empty-subgraph rendering

Testing

  • Not run in this environment: local Python is 3.9, while this repo requires 3.10+ for test imports.

Closes #35492

Changed files

  • libs/core/langchain_core/runnables/graph_mermaid.py (modified, +44/-28)
  • libs/core/tests/unit_tests/runnables/test_graph.py (modified, +36/-0)

PR #35596: fix: Nested subgraph rendering fails for 3+ levels with `draw_merma...

Description (problem / solution / changelog)

Summary

This addresses #35492.

What changed

<!-- Claude filled in the implementation details at commit time -->

Implemented a fix based on the issue description. See the diff for specifics.

Testing

  • Verified against the existing test suite
  • Checked that the fix addresses the reported behavior

Changed files

  • libs/core/langchain_core/runnables/graph_mermaid.py (modified, +27/-6)
  • libs/core/tests/unit_tests/runnables/__snapshots__/test_graph.ambr (modified, +23/-0)
  • libs/core/tests/unit_tests/runnables/test_graph.py (modified, +32/-0)

PR #35933: fix(core): fix nested subgraph rendering for 3+ levels with draw_mermaid

Description (problem / solution / changelog)

Summary

Fixes #35492.

Replaces the flat "empty subgraphs" loop in draw_mermaid with a recursive helper that correctly renders subgraph/end nesting for 3+ level deep graphs where no subgraph has internal edges.

Root Cause

draw_mermaid has two rendering paths for subgraphs:

  1. add_subgraph() (recursive) — handles subgraphs that contain edges between their own nodes.
  2. "empty subgraphs" loop — handles subgraphs whose nodes only connect to nodes outside the subgraph.

The bug lives in path 2. The loop contained:

if ":" not in prefix and prefix not in seen_subgraphs:

For 3+ level nesting (e.g. parent:child:grandchild as the single internal node), the prefix is parent:child — a multi-level key containing a colon. The condition ":" not in prefix evaluates to False, so the subgraph box for both parent and child is never emitted. The node renders as a flat, escaped ID (parent\3achild\3agrandchild) rather than being nested inside proper subgraph boxes.

2-level nesting (sub:leaf) was unaffected because its prefix is sub — no colon.

Solution

Replaced the flat loop with a recursive helper add_empty_subgraph_recursive:

  1. Before the loop, expand every unseen prefix into all ancestor levels (e.g. parent:child{"parent", "parent:child"}).
  2. Start from each top-level prefix (no colon) and recurse into direct children, emitting subgraph/end pairs at each depth.
  3. Nodes are still rendered with the same single-tab indent as before — no snapshot changes for existing 2-level tests.

Testing

  • Added test_deep_single_node_subgraph_mermaid reproducing the exact scenario from the issue: a 3-level graph (__start__ → parent:child:grandchild → __end__) with no internal subgraph edges.
  • All 22 existing graph unit tests pass unchanged.
  • Assertions verify both subgraph parent and subgraph child appear in the output.
pytest libs/core/tests/unit_tests/runnables/test_graph.py -v
# 22 passed

Checklist

  • Fixes the root cause (not just the symptom)
  • New test covers the exact failing scenario from the issue
  • All existing tests pass
  • No unrelated changes
  • Code style matches project conventions
  • Read CONTRIBUTING.md

Note: This PR was created with the assistance of an AI agent (Claude Code). The root cause analysis, implementation, and tests were reviewed and verified by running the full test suite locally.

Changed files

  • libs/core/langchain_core/runnables/graph_mermaid.py (modified, +42/-8)
  • libs/core/tests/unit_tests/runnables/__snapshots__/test_graph.ambr (modified, +23/-0)
  • libs/core/tests/unit_tests/runnables/test_graph.py (modified, +45/-0)

Code Example

from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START, END
from IPython.display import Image, display


# --- Grandchild graph ---
class GrandChildState(TypedDict):
    my_grandchild_key: str


def grandchild_1(state: GrandChildState) -> GrandChildState:
    return {"my_grandchild_key": f'([GrandChild] {state["my_grandchild_key"]})'}


grandchild = StateGraph(GrandChildState)
grandchild.add_node("grandchild", grandchild_1)
grandchild.add_edge(START, "grandchild")
grandchild.add_edge("grandchild", END)
grandchild_graph = grandchild.compile()


# --- Child graph (calls grandchild) ---
class ChildState(TypedDict):
    my_child_key: str


def call_grandchild_graph(state: ChildState) -> ChildState:
    grandchild_graph_input = {"my_grandchild_key": state["my_child_key"]}
    grandchild_graph_output = grandchild_graph.invoke(grandchild_graph_input)
    return {"my_child_key": f'([Child] {grandchild_graph_output["my_grandchild_key"]})'}


child = StateGraph(ChildState)
child.add_node("child", call_grandchild_graph)
child.add_edge(START, "child")
child.add_edge("child", END)
child_graph = child.compile()


# --- Parent graph (calls child) ---
class ParentState(TypedDict):
    my_parent_key: str


def call_child_graph(state: ParentState) -> ParentState:
    child_graph_input = {"my_child_key": state["my_parent_key"]}
    child_graph_output = child_graph.invoke(child_graph_input)
    return {"my_parent_key": f'([Parent] {child_graph_output["my_child_key"]})'}


parent = StateGraph(ParentState)
parent.add_node("parent", call_child_graph)
parent.add_edge(START, "parent")
parent.add_edge("parent", END)
parent_graph = parent.compile()


# --- Reproduce the bug ---

# 2-level nesting: renders correctly
display(Image(child_graph.get_graph(xray=True).draw_mermaid_png()))

# 3-level nesting: node IDs show \3a instead of readable names
display(Image(parent_graph.get_graph(xray=True).draw_mermaid_png()))

# Check the raw Mermaid syntax to see the escaped IDs
print(parent_graph.get_graph(xray=True).draw_mermaid())

---

No error is raised. The issue is a **rendering defect** — the generated Mermaid diagram fails to properly render nested subgraph boxes for 3 or more levels of nesting.

For 3+ level nesting, the diagram shows flat node IDs with escaped colons instead of properly nested subgraph containers:


graph TD;
	__start__([<p>__start__</p>]):::first
	__end__([<p>__end__</p>]):::last
	__start__ --> parent\3achild\3agrandchild;
	parent\3achild\3agrandchild --> __end__;


No subgraph boxes are created at all — the nested node appears as a flat element with `\3a` (CSS-escaped colon) in its ID.

---

if ":" not in prefix:
    # ... create subgraph box
RAW_BUFFERClick to expand / collapse

Checked other resources

  • This is a bug, not a usage question.
  • I added a clear and descriptive title that summarizes this issue.
  • I used the GitHub search to find a similar question and didn't find it.
  • I am sure that this is a bug in LangChain rather than my code.
  • The bug is not resolved by updating to the latest stable version of LangChain (or the specific integration package).
  • This is not related to the langchain-community package.
  • I posted a self-contained, minimal, reproducible example. A maintainer can copy it and run it AS IS.

Package (Required)

  • langchain
  • langchain-openai
  • langchain-anthropic
  • langchain-classic
  • langchain-core
  • langchain-model-profiles
  • langchain-tests
  • langchain-text-splitters
  • langchain-chroma
  • langchain-deepseek
  • langchain-exa
  • langchain-fireworks
  • langchain-groq
  • langchain-huggingface
  • langchain-mistralai
  • langchain-nomic
  • langchain-ollama
  • langchain-openrouter
  • langchain-perplexity
  • langchain-qdrant
  • langchain-xai
  • Other / not sure / general

Related Issues / PRs

No response

Reproduction Steps / Example Code (Python)

from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START, END
from IPython.display import Image, display


# --- Grandchild graph ---
class GrandChildState(TypedDict):
    my_grandchild_key: str


def grandchild_1(state: GrandChildState) -> GrandChildState:
    return {"my_grandchild_key": f'([GrandChild] {state["my_grandchild_key"]})'}


grandchild = StateGraph(GrandChildState)
grandchild.add_node("grandchild", grandchild_1)
grandchild.add_edge(START, "grandchild")
grandchild.add_edge("grandchild", END)
grandchild_graph = grandchild.compile()


# --- Child graph (calls grandchild) ---
class ChildState(TypedDict):
    my_child_key: str


def call_grandchild_graph(state: ChildState) -> ChildState:
    grandchild_graph_input = {"my_grandchild_key": state["my_child_key"]}
    grandchild_graph_output = grandchild_graph.invoke(grandchild_graph_input)
    return {"my_child_key": f'([Child] {grandchild_graph_output["my_grandchild_key"]})'}


child = StateGraph(ChildState)
child.add_node("child", call_grandchild_graph)
child.add_edge(START, "child")
child.add_edge("child", END)
child_graph = child.compile()


# --- Parent graph (calls child) ---
class ParentState(TypedDict):
    my_parent_key: str


def call_child_graph(state: ParentState) -> ParentState:
    child_graph_input = {"my_child_key": state["my_parent_key"]}
    child_graph_output = child_graph.invoke(child_graph_input)
    return {"my_parent_key": f'([Parent] {child_graph_output["my_child_key"]})'}


parent = StateGraph(ParentState)
parent.add_node("parent", call_child_graph)
parent.add_edge(START, "parent")
parent.add_edge("parent", END)
parent_graph = parent.compile()


# --- Reproduce the bug ---

# 2-level nesting: renders correctly
display(Image(child_graph.get_graph(xray=True).draw_mermaid_png()))

# 3-level nesting: node IDs show \3a instead of readable names
display(Image(parent_graph.get_graph(xray=True).draw_mermaid_png()))

# Check the raw Mermaid syntax to see the escaped IDs
print(parent_graph.get_graph(xray=True).draw_mermaid())

Error Message and Stack Trace (if applicable)

No error is raised. The issue is a **rendering defect** — the generated Mermaid diagram fails to properly render nested subgraph boxes for 3 or more levels of nesting.

For 3+ level nesting, the diagram shows flat node IDs with escaped colons instead of properly nested subgraph containers:


graph TD;
	__start__([<p>__start__</p>]):::first
	__end__([<p>__end__</p>]):::last
	__start__ --> parent\3achild\3agrandchild;
	parent\3achild\3agrandchild --> __end__;


No subgraph boxes are created at all — the nested node appears as a flat element with `\3a` (CSS-escaped colon) in its ID.

Description

When visualizing a LangGraph with 3 or more levels of nested subgraphs using get_graph(xray=True).draw_mermaid(), the Mermaid output fails to create proper subgraph boxes for the nested hierarchy. Instead, nodes appear as flat elements with escaped colon characters in their IDs.

Root Cause

The Graph.extend() method creates hierarchical node IDs by joining names with colons (e.g., parent:child:grandchild).

The draw_mermaid() function in langchain_core/runnables/graph_mermaid.py has two code paths for subgraph rendering:

  1. add_subgraph() function (recursive) - Handles subgraphs that contain internal edges between their nodes
  2. "Empty subgraphs" section (around line 239) - Handles subgraphs where all nodes connect to nodes outside the subgraph (no internal edges)

The bug exists in the "empty subgraphs" section. The original code used this condition:

if ":" not in prefix:
    # ... create subgraph box

This condition completely skipped multi-level prefixes like parent:child. Since each graph wraps its child as a single subgraph node, the nested subgraph's nodes have no internal edges (they connect only to parent nodes), which forces the code into the "empty subgraphs" path. With ":" not in prefix being false for parent:child, the subgraph boxes never get created in the Mermaid output.

As a result, 3+ level nested subgraphs don't render their subgraph containers, causing the nodes to appear flat with escaped colons in their IDs (parent\3achild\3agrandchild).

Behavior Comparison

Nesting LevelBehaviorStatus
2 levels (child → grandchild)Subgraph boxes render correctlyOK
3 levels (parent → child → grandchild)Subgraph boxes not created, nodes shown as flat with escaped colonsBug

The issue manifests when nested subgraph nodes have no internal edges between them, which forces the "empty subgraphs" code path.

Screenshots

2-level nesting (correct):

The child graph with xray=True correctly renders grandchild inside a child subgraph box.

<img width="190" height="284" alt="Image" src="https://github.com/user-attachments/assets/b68109d6-908d-4bb4-a14f-2ded9b8aa878" />

3-level nesting (bug):

The parent graph with xray=True shows parent\3achild\3agrandchild as a flat node label instead of properly nesting grandchild within subgraph boxes.

<img width="273" height="234" alt="Image" src="https://github.com/user-attachments/assets/0820ae69-0cb4-425e-a55b-2e351092b254" />

System Info

System Information

OS: Darwin OS Version: Darwin Kernel Version 24.6.0: Mon Jul 14 11:30:40 PDT 2025; root:xnu-11417.140.69~1/RELEASE_ARM64_T6041 Python Version: 3.13.10 (main, Dec 2 2025, 22:01:27) [Clang 21.1.4 ]

Package Information

langchain_core: 1.2.16 langsmith: 0.7.9 langgraph_sdk: 0.3.9

Optional packages not installed

deepagents deepagents-cli

Other Dependencies

httpx: 0.28.1 jsonpatch: 1.33 orjson: 3.11.7 packaging: 26.0 pydantic: 2.12.5 pyyaml: 6.0.3 requests: 2.32.5 requests-toolbelt: 1.0.0 tenacity: 9.1.4 typing-extensions: 4.15.0 uuid-utils: 0.14.1 xxhash: 3.6.0 zstandard: 0.25.0

langgraph: 1.0.10

extent analysis

Problem Summary

The issue is a rendering defect in LangGraph's Mermaid diagram generation. When visualizing a graph with 3 or more levels of nested subgraphs, the diagram fails to create proper subgraph boxes, instead showing flat node IDs with escaped colons.

Root Cause Analysis

The bug exists in the "empty subgraphs" section of langchain_core/runnables/graph_mermaid.py. The condition ":" not in prefix is used to determine whether to create a subgraph box. However, this condition is false for multi-level prefixes like parent:child, causing the subgraph boxes to never get created.

Fix Plan

Step 1: Update langchain_core/runnables/graph_mermaid.py

Modify the "empty subgraphs" section to correctly handle multi-level prefixes. Replace the condition ":" not in prefix with a more robust check that considers the presence of colons in the prefix.

if ":" not in prefix:
    # ... create subgraph box

should be replaced with

if not prefix.count(":") == 1:
    # ... create subgraph box

Step 2: Verify the fix

Run the reproduction steps with 3-level nesting and verify that the subgraph boxes are created correctly.

# 3-level nesting: node IDs show readable names
display(Image(parent_graph.get_graph(xray=True).draw_mermaid_png()))

should render the subgraph boxes correctly.

Verification

To verify the fix, check the following:

  • Run the reproduction steps with 3-level nesting and verify that the subgraph boxes are created correctly.
  • Check the raw Mermaid syntax to ensure that the subgraph boxes are properly rendered.
  • Verify that the fix does not introduce any regressions in the 2-level nesting case.

Extra Tips

  • When working with nested subgraphs, ensure that the add_edge

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

langchain - ✅(Solved) Fix Nested subgraph rendering fails for 3+ levels with `draw_mermaid` and `xray=True` [6 pull requests, 4 comments, 3 participants]