Command do not render graph if nodes are defined within a class. #391

Closed
opened 2026-02-20 17:39:53 -05:00 by yindo · 3 comments
Owner

Originally created by @SergioG-M on GitHub (Jan 9, 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 random

from langgraph.graph import START, StateGraph
from langgraph.types import Command
from typing_extensions import Literal, TypedDict


# Define graph state
class State(TypedDict):
    foo: str


# Define the nodes


class MyGraph:
    def node_a(self, state: State) -> Command[Literal["node_b", "node_c"]]:
        print("Called A")
        value = random.choice(["a", "b"])
        # this is a replacement for a conditional edge function
        if value == "a":
            goto = "node_b"
        else:
            goto = "node_c"

        # note how Command allows you to BOTH update the graph state AND route to the next node
        return Command(
            # this is the state update
            update={"foo": value},
            # this is a replacement for an edge
            goto=goto,
        )

    # Nodes B and C are unchanged

    def node_b(self, state: State):
        print("Called B")
        return {"foo": state["foo"] + "b"}

    def node_c(self, state: State):
        print("Called C")
        return {"foo": state["foo"] + "c"}

    def build_graph(self):
        builder = StateGraph(State)
        builder.add_edge(START, "node_a")
        builder.add_node("node_a", self.node_a)
        builder.add_node("node_b", self.node_b)
        builder.add_node("node_c", self.node_c)
        # NOTE: there are no edges between nodes A, B and C!

        graph = builder.compile()
        return graph


from IPython.display import display, Image

graph = MyGraph().build_graph()

display(Image(graph.get_graph().draw_mermaid_png()))

Error Message and Stack Trace (if applicable)

No response

Description

When using command inside a class the edges are not rendered. It works if nodes are outside (or probably if static methods?)
I adapted this code: https://langchain-ai.github.io/langgraph/how-tos/command/#define-graph
And the generated graph is this:

image

System Info

langgraph version 0.2.61

Originally created by @SergioG-M on GitHub (Jan 9, 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 random from langgraph.graph import START, StateGraph from langgraph.types import Command from typing_extensions import Literal, TypedDict # Define graph state class State(TypedDict): foo: str # Define the nodes class MyGraph: def node_a(self, state: State) -> Command[Literal["node_b", "node_c"]]: print("Called A") value = random.choice(["a", "b"]) # this is a replacement for a conditional edge function if value == "a": goto = "node_b" else: goto = "node_c" # note how Command allows you to BOTH update the graph state AND route to the next node return Command( # this is the state update update={"foo": value}, # this is a replacement for an edge goto=goto, ) # Nodes B and C are unchanged def node_b(self, state: State): print("Called B") return {"foo": state["foo"] + "b"} def node_c(self, state: State): print("Called C") return {"foo": state["foo"] + "c"} def build_graph(self): builder = StateGraph(State) builder.add_edge(START, "node_a") builder.add_node("node_a", self.node_a) builder.add_node("node_b", self.node_b) builder.add_node("node_c", self.node_c) # NOTE: there are no edges between nodes A, B and C! graph = builder.compile() return graph from IPython.display import display, Image graph = MyGraph().build_graph() display(Image(graph.get_graph().draw_mermaid_png())) ``` ### Error Message and Stack Trace (if applicable) _No response_ ### Description When using command inside a class the edges are not rendered. It works if nodes are outside (or probably if static methods?) I adapted this code: https://langchain-ai.github.io/langgraph/how-tos/command/#define-graph And the generated graph is this: ![image](https://github.com/user-attachments/assets/d3a850b5-802f-4bc1-a2a1-37c2c8d0d5d7) ### System Info langgraph version 0.2.61
yindo closed this issue 2026-02-20 17:39:53 -05:00
Author
Owner

@vbarda commented on GitHub (Jan 9, 2025):

I can reproduce the issue, will look into it

@vbarda commented on GitHub (Jan 9, 2025): I can reproduce the issue, will look into it
Author
Owner

@vbarda commented on GitHub (Jan 14, 2025):

Fixed in #3032

@vbarda commented on GitHub (Jan 14, 2025): Fixed in #3032
Author
Owner

@luhodaan commented on GitHub (Jul 30, 2025):

Hi,
I'm experiencing the same issue described in this thread, but with LangGraph 0.3.1. The edges are still not being rendered when using Command within class methods.
Environment:

LangGraph version: 0.3.1
Python version: 3.11.6
OS: Windows 11

Real-world impact:
I have a multi-step agent with conditional routing based on LLM tool calls and structured outputs. The graph works perfectly but all Command-inferred edges are invisible, making it impossible to understand the flow in LangGraph Studio.

Could we please reopen this issue?

I attach a simplified example of my graph and what I mean:

from langgraph.types import Command
from typing import Literal

def evaluate_user_query(state, llm_instance) -> Command[Literal["tables_or_rdf", "END"]]:
   evaluation_result = llm_instance.with_structured_output(QueryEvaluation).invoke(state["messages"])
   
   goto = "tables_or_rdf" if evaluation_result.is_valid else "END"
   return Command(update={"query_evaluation": evaluation_result}, goto=goto)

def tables_or_rdf(state, llm_instance, list_tables_tool, brick_explore_tool) -> Command[Literal["list_tables_tool", "brick_explore_tool", "END"]]:
   llm_with_tools = llm_instance.bind_tools([list_tables_tool, brick_explore_tool])
   response = llm_with_tools.invoke(state["messages"])
   
   goto = "END"
   if hasattr(response, 'tool_calls') and response.tool_calls:
       tool_name = response.tool_calls[0].get("name")
       if tool_name == "brick_explore_tool":
           goto = "brick_explore_tool"
       elif tool_name in ["list_tables_tool", "sql_db_list_tables"]:
           goto = "list_tables_tool"
   
   return Command(update={"messages": [response]}, goto=goto)

def generate_query(state, llm_instance, run_query_tool, brick_explore_tool) -> Command[Literal["check_query","brick_explore_tool", "END"]]:
   llm_with_tools = llm_instance.bind_tools([run_query_tool, brick_explore_tool])
   response = llm_with_tools.invoke(state["messages"])

   goto = "END"
   if hasattr(response, 'tool_calls') and response.tool_calls:
       tool_call = response.tool_calls[0]
       if tool_call.get("name") == "sql_db_query":
           goto = "check_query"
       elif tool_call.get("name") == "brick_explore_tool":
           goto = "brick_explore_tool"

   return Command(update={"messages": [response]}, goto=goto)

class MyGraph:
   def build_graph(self):
       workflow = StateGraph(MessagesState)
       workflow.add_node("evaluate_user_query", self.evaluate_user_query)
       workflow.add_node("tables_or_rdf", self.tables_or_rdf)
       workflow.add_node("generate_query", self.generate_query)
       workflow.add_node("list_tables_tool", list_tables_node)
       workflow.add_node("brick_explore_tool", brick_explore_node)
       workflow.add_node("check_query", self.check_query)
       
       workflow.add_edge(START, "evaluate_user_query")
       workflow.add_edge("list_tables_tool", "generate_query")
       workflow.add_edge("check_query", "run_query")
       
       return workflow.compile()
@luhodaan commented on GitHub (Jul 30, 2025): Hi, I'm experiencing the same issue described in this thread, but with LangGraph 0.3.1. The edges are still not being rendered when using Command within class methods. Environment: LangGraph version: 0.3.1 Python version: 3.11.6 OS: Windows 11 Real-world impact: I have a multi-step agent with conditional routing based on LLM tool calls and structured outputs. The graph works perfectly but all Command-inferred edges are invisible, making it impossible to understand the flow in LangGraph Studio. **Could we please reopen this issue?** I attach a simplified example of my graph and what I mean: ``` from langgraph.types import Command from typing import Literal def evaluate_user_query(state, llm_instance) -> Command[Literal["tables_or_rdf", "END"]]: evaluation_result = llm_instance.with_structured_output(QueryEvaluation).invoke(state["messages"]) goto = "tables_or_rdf" if evaluation_result.is_valid else "END" return Command(update={"query_evaluation": evaluation_result}, goto=goto) def tables_or_rdf(state, llm_instance, list_tables_tool, brick_explore_tool) -> Command[Literal["list_tables_tool", "brick_explore_tool", "END"]]: llm_with_tools = llm_instance.bind_tools([list_tables_tool, brick_explore_tool]) response = llm_with_tools.invoke(state["messages"]) goto = "END" if hasattr(response, 'tool_calls') and response.tool_calls: tool_name = response.tool_calls[0].get("name") if tool_name == "brick_explore_tool": goto = "brick_explore_tool" elif tool_name in ["list_tables_tool", "sql_db_list_tables"]: goto = "list_tables_tool" return Command(update={"messages": [response]}, goto=goto) def generate_query(state, llm_instance, run_query_tool, brick_explore_tool) -> Command[Literal["check_query","brick_explore_tool", "END"]]: llm_with_tools = llm_instance.bind_tools([run_query_tool, brick_explore_tool]) response = llm_with_tools.invoke(state["messages"]) goto = "END" if hasattr(response, 'tool_calls') and response.tool_calls: tool_call = response.tool_calls[0] if tool_call.get("name") == "sql_db_query": goto = "check_query" elif tool_call.get("name") == "brick_explore_tool": goto = "brick_explore_tool" return Command(update={"messages": [response]}, goto=goto) class MyGraph: def build_graph(self): workflow = StateGraph(MessagesState) workflow.add_node("evaluate_user_query", self.evaluate_user_query) workflow.add_node("tables_or_rdf", self.tables_or_rdf) workflow.add_node("generate_query", self.generate_query) workflow.add_node("list_tables_tool", list_tables_node) workflow.add_node("brick_explore_tool", brick_explore_node) workflow.add_node("check_query", self.check_query) workflow.add_edge(START, "evaluate_user_query") workflow.add_edge("list_tables_tool", "generate_query") workflow.add_edge("check_query", "run_query") return workflow.compile() ```
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: langchain-ai/langgraph#391