When using Command, the graph is not drawn properly #518

Closed
opened 2026-02-20 17:40:32 -05:00 by yindo · 2 comments
Owner

Originally created by @wjaskowski on GitHub (Mar 17, 2025).

Checked other resources

  • This is a bug, not a usage question. For questions, please use GitHub Discussions.
  • 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 os
from typing import Annotated

from langchain_community.chat_models import ChatLiteLLMRouter
from langchain_core.messages import HumanMessage
from langgraph.types import Command
from litellm import Router
from typing_extensions import TypedDict

from langgraph.graph import StateGraph
from langgraph.graph.message import add_messages, MessagesState
from langchain_openai import ChatOpenAI



def _set_env(var: str):
    if not os.environ.get(var):
        os.environ[var] = getpass.getpass(f"{var}: ")


_set_env("OPENAI_API_KEY")

llm = ChatOpenAI(model="gpt-4o")

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

def node_1(state: State):
    response = llm.invoke(state["messages"])
    return Command(
        update={"messages": response},
        goto="subgraph_1",
    )

def node_2(state: State):
    return {"messages": [llm.invoke(state["messages"])]}

def subnode_1(state: State):
    return {"messages": ['subnode 1 message']}

def subnode_2(state: State):
    return {"messages": ['subnode 2 message']}

def subgraph_1(state: State):
    response = subgraph.invoke({'messages': state["messages"]})
    return {"messages": response['messages'][-1]}

subgraph = (StateGraph(MessagesState)
    .add_node(subnode_1)
    .add_node(subnode_2)
    .set_entry_point("subnode_1")
    .add_edge("subnode_1", "subnode_2")
    .set_finish_point("subnode_2")
).compile()

graph = (StateGraph(State)
    .add_node(node_1)
    .add_node(subgraph_1)
    .add_node(node_2)
    .set_entry_point("node_1")
    #.add_edge("node_1", "subgraph_1")
    .add_edge("subgraph_1", "node_2")
    .set_finish_point("node_2")
 ).compile()

graph.get_graph(xray=True).draw_mermaid_png(output_file_path='test_subgraph.png')

for s in graph.stream({"messages": [HumanMessage(content="What is Langfuse in one short sentence?")]}, stream_mode="updates", subgraphs=True):
    print('----')
    print(type(s), s)
    #print('\n'.join([str(x) for x in s['messages']]))

Error Message and Stack Trace (if applicable)


Description

The graph works correctly but it is incorrectly displayed:
Image

System Info

System Information
------------------
> OS:  Darwin
> OS Version:  Darwin Kernel Version 24.3.0: Thu Jan  2 20:24:16 PST 2025; root:xnu-11215.81.4~3/RELEASE_ARM64_T6000
> Python Version:  3.11.9 (main, Apr  2 2024, 08:25:04) [Clang 15.0.0 (clang-1500.3.9.4)]

Package Information
-------------------
> langchain_core: 0.3.45
> langchain: 0.3.20
> langchain_community: 0.3.19
> langsmith: 0.3.15
> langchain_openai: 0.3.8
> langchain_text_splitters: 0.3.6
> langgraph_reflection: 0.0.1
> langgraph_sdk: 0.1.57
Originally created by @wjaskowski on GitHub (Mar 17, 2025). ### Checked other resources - [x] This is a bug, not a usage question. For questions, please use GitHub Discussions. - [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 os from typing import Annotated from langchain_community.chat_models import ChatLiteLLMRouter from langchain_core.messages import HumanMessage from langgraph.types import Command from litellm import Router from typing_extensions import TypedDict from langgraph.graph import StateGraph from langgraph.graph.message import add_messages, MessagesState from langchain_openai import ChatOpenAI def _set_env(var: str): if not os.environ.get(var): os.environ[var] = getpass.getpass(f"{var}: ") _set_env("OPENAI_API_KEY") llm = ChatOpenAI(model="gpt-4o") class State(TypedDict): messages: Annotated[list, add_messages] def node_1(state: State): response = llm.invoke(state["messages"]) return Command( update={"messages": response}, goto="subgraph_1", ) def node_2(state: State): return {"messages": [llm.invoke(state["messages"])]} def subnode_1(state: State): return {"messages": ['subnode 1 message']} def subnode_2(state: State): return {"messages": ['subnode 2 message']} def subgraph_1(state: State): response = subgraph.invoke({'messages': state["messages"]}) return {"messages": response['messages'][-1]} subgraph = (StateGraph(MessagesState) .add_node(subnode_1) .add_node(subnode_2) .set_entry_point("subnode_1") .add_edge("subnode_1", "subnode_2") .set_finish_point("subnode_2") ).compile() graph = (StateGraph(State) .add_node(node_1) .add_node(subgraph_1) .add_node(node_2) .set_entry_point("node_1") #.add_edge("node_1", "subgraph_1") .add_edge("subgraph_1", "node_2") .set_finish_point("node_2") ).compile() graph.get_graph(xray=True).draw_mermaid_png(output_file_path='test_subgraph.png') for s in graph.stream({"messages": [HumanMessage(content="What is Langfuse in one short sentence?")]}, stream_mode="updates", subgraphs=True): print('----') print(type(s), s) #print('\n'.join([str(x) for x in s['messages']])) ``` ### Error Message and Stack Trace (if applicable) ```shell ``` ### Description The graph works correctly but it is incorrectly displayed: ![Image](https://github.com/user-attachments/assets/cb47ae59-1ee3-4062-bc8a-f79717ff37c8) ### System Info ``` System Information ------------------ > OS: Darwin > OS Version: Darwin Kernel Version 24.3.0: Thu Jan 2 20:24:16 PST 2025; root:xnu-11215.81.4~3/RELEASE_ARM64_T6000 > Python Version: 3.11.9 (main, Apr 2 2024, 08:25:04) [Clang 15.0.0 (clang-1500.3.9.4)] Package Information ------------------- > langchain_core: 0.3.45 > langchain: 0.3.20 > langchain_community: 0.3.19 > langsmith: 0.3.15 > langchain_openai: 0.3.8 > langchain_text_splitters: 0.3.6 > langgraph_reflection: 0.0.1 > langgraph_sdk: 0.1.57 ```
yindo closed this issue 2026-02-20 17:40:32 -05:00
Author
Owner

@vbarda commented on GitHub (Mar 17, 2025):

use

.add_node(node_1, destinations=("subgraph_1",))

or add a type annotation

def node_1(state: State) -> Command[Literal["subgraph_1"]]:
@vbarda commented on GitHub (Mar 17, 2025): use ```python .add_node(node_1, destinations=("subgraph_1",)) ``` or add a type annotation ```python def node_1(state: State) -> Command[Literal["subgraph_1"]]: ```
Author
Owner

@wjaskowski commented on GitHub (Mar 17, 2025):

I see. Thx.

I feel your pain with Python here. Control through explicitly constructing graph is inconvenient but is it the only way to get the control graph without executing it. On the other hand, Command allows to construct the graph in Python but since this code is not parsed the control/graph is not know before the actual execution.

Both options you provided has the disadvantage of easily getting into a situation when the reality differs from the actual graph. This would bite me at some point. I think I will stay with more cumbersome conditional edges rather then using Commands just to be sure that I really have what I see.

@wjaskowski commented on GitHub (Mar 17, 2025): I see. Thx. I feel your pain with Python here. Control through explicitly constructing graph is inconvenient but is it the only way to get the control graph without executing it. On the other hand, Command allows to construct the graph in Python but since this code is not parsed the control/graph is not know before the actual execution. Both options you provided has the disadvantage of easily getting into a situation when the reality differs from the actual graph. This would bite me at some point. I think I will stay with more cumbersome conditional edges rather then using Commands just to be sure that I really have what I see.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: langchain-ai/langgraph#518