-
Notifications
You must be signed in to change notification settings - Fork 4.3k
Description
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 LangGraph rather than my code.
- The bug is not resolved by updating to the latest stable version of LangGraph (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.
Reproduction Steps / Example Code (Python)
from typing import TypedDict, Annotated
import operator
from langgraph.graph import StateGraph, START, END
# =============================================================================
# ISSUE 1: Unreachable Node - silently never executes
# =============================================================================
print("=" * 60)
print("ISSUE 1: Unreachable Node")
print("=" * 60)
class State1(TypedDict):
messages: Annotated[list[str], operator.add]
def important_processing(state: State1) -> dict:
print(" -> important_processing executed")
return {"messages": ["processed"]}
def orphan_node(state: State1) -> dict:
"""This node should process critical logic but is never reachable!"""
print(" -> orphan_node executed") # This will NEVER print!
return {"messages": ["orphan was here"]}
graph1 = StateGraph(State1)
graph1.add_node("process", important_processing)
graph1.add_node("orphan", orphan_node) # <- Forgot to connect this!
graph1.add_edge(START, "process")
graph1.add_edge("process", END)
# Compiles without any error or warning!
compiled1 = graph1.compile()
print("Graph compiled successfully (no warning about orphan node)")
result1 = compiled1.invoke({"messages": []})
print(f"Result: {result1}")
print("Notice: 'orphan_node executed' was never printed - silent bug!")
# =============================================================================
# ISSUE 2: Dead End Node - no explicit path to END (possible design mistake)
# =============================================================================
print("\n" + "=" * 60)
print("ISSUE 2: Dead End Node (sink without explicit path to END)")
print("=" * 60)
class State2(TypedDict):
value: Annotated[list[str], operator.add]
def main_path(state: State2) -> dict:
print(" -> main_path executed")
return {"value": ["main"]}
def dead_end(state: State2) -> dict:
"""This node executes but has no explicit edge to END."""
print(" -> dead_end executed")
return {"value": ["dead_end_result"]}
graph2 = StateGraph(State2)
graph2.add_node("main", main_path)
graph2.add_node("dead_end", dead_end)
graph2.add_edge(START, "main")
graph2.add_edge(START, "dead_end") # Parallel branch
graph2.add_edge("main", END)
# Missing: graph2.add_edge("dead_end", END)
# Compiles without any error or warning!
compiled2 = graph2.compile()
print("Graph compiled successfully (no warning about dead end)")
result2 = compiled2.invoke({"value": []})
print(f"Result: {result2}")
print("Node executes, but missing edge to END might indicate a design mistake!")
# =============================================================================
# ISSUE 3: Completely disconnected island of nodes
# =============================================================================
print("\n" + "=" * 60)
print("ISSUE 3: Island of interconnected but unreachable nodes")
print("=" * 60)
class State3(TypedDict):
count: int
def real_work(state: State3) -> dict:
print(" -> real_work executed")
return {"count": state["count"] + 1}
def island_a(state: State3) -> dict:
print(" -> island_a executed")
return {"count": state["count"] + 10}
def island_b(state: State3) -> dict:
print(" -> island_b executed")
return {"count": state["count"] + 20}
graph3 = StateGraph(State3)
graph3.add_node("real_work", real_work)
graph3.add_node("island_a", island_a)
graph3.add_node("island_b", island_b)
# Connected part
graph3.add_edge(START, "real_work")
graph3.add_edge("real_work", END)
# Disconnected island - these nodes connect to each other but not to START
graph3.add_edge("island_a", "island_b")
graph3.add_edge("island_b", END)
# Compiles without any error or warning!
compiled3 = graph3.compile()
print("Graph compiled successfully (no warning about island)")
result3 = compiled3.invoke({"count": 0})
print(f"Result: {result3}")
print("The entire island (island_a -> island_b) never executed - silent bug!")
print("\n" + "=" * 60)
print("SUMMARY: All graphs compiled without warnings about detached nodes.")
print("This can lead to hard-to-debug issues in production.")
print("=" * 60)Error Message and Stack Trace (if applicable)
Description
# ISSUES 1 and 3: Unreachable Node(s) are silently ignored. This shouldn't happen.
# ISSUE 2: Dead End Node with no explicit mapping to END are silently mapped to END. This could be a design mistake on the developer's end, which will also happen silently. This shouldn't happen.
Suggestion: Introduce optional validation to warn or raise an exception on detached nodes. These are either unreachable or dead-ends not explicitly mapping to END. When developers specify the routing of their nodes, or that routing can be inferred from the node spec, it's used to detect detached nodes and handle it so that these don't go unnoticed. For developers that don't wish to specify the routing of the nodes (e.g., when routing by Command or conditional edges), they could set the dedicated compilation parameter to "ignore" to disable the node validation.
Here is a concrete proposal: #6736
System Info
System Information
OS: Darwin
OS Version: Darwin Kernel Version 24.6.0: Wed Nov 5 21:33:58 PST 2025; root:xnu-11417.140.69.705.2~1/RELEASE_ARM64_T6000
Python Version: 3.12.12 (main, Dec 11 2025, 14:17:00) [Clang 17.0.0 (clang-1700.4.4.1)]
Package Information
langchain_core: 1.2.7
langsmith: 0.6.4
langgraph_api: 0.6.39
langgraph_cli: 0.4.12
langgraph_runtime_inmem: 0.22.1
langgraph_sdk: 0.3.3
Optional packages not installed
langserve
Other Dependencies
blockbuster: 1.5.26
click: 8.3.1
cloudpickle: 3.1.2
cryptography: 46.0.3
grpcio: 1.76.0
grpcio-health-checking: 1.76.0
grpcio-tools: 1.75.1
httpx: 0.28.1
jsonpatch: 1.33
jsonschema-rs: 0.29.1
langgraph: 1.0.7
langgraph-checkpoint: 4.0.0
opentelemetry-api: 1.39.1
opentelemetry-exporter-otlp-proto-http: 1.39.1
opentelemetry-sdk: 1.39.1
orjson: 3.11.5
packaging: 25.0
protobuf: 6.33.4
pydantic: 2.12.5
pyjwt: 2.10.1
pytest: 9.0.2
python-dotenv: 1.2.1
pyyaml: 6.0.3
requests: 2.32.5
requests-toolbelt: 1.0.0
sse-starlette: 2.1.3
starlette: 0.51.0
structlog: 25.5.0
tenacity: 9.1.2
truststore: 0.10.4
typing-extensions: 4.15.0
uuid-utils: 0.13.0
uvicorn: 0.40.0
watchfiles: 1.1.1
zstandard: 0.25.0