microsoft/autogen · error · ValueError
Graph must have at least one leaf node
Error message
Graph must have at least one leaf node
What it means
Raised by DiGraph.graph_validate when the graph has no leaf node — a node with no outgoing edges. Leaf nodes are where the workflow terminates; a graph in which every node has at least one outgoing edge (pure cycles, or a 'final' node that was accidentally given an edge) never formally ends and is rejected.
Source
Thrown at python/packages/autogen-agentchat/src/autogen_agentchat/teams/_group_chat/_graph/_digraph_group_chat.py:216
return has_cycle
def get_has_cycles(self) -> bool:
"""Indicates if the graph has at least one cycle (with valid exit conditions)."""
if self._has_cycles is None:
self._has_cycles = self.has_cycles_with_exit()
return self._has_cycles
def graph_validate(self) -> None:
"""Validate graph structure and execution rules."""
if not self.nodes:
raise ValueError("Graph has no nodes.")
if not self.get_start_nodes():
raise ValueError("Graph must have at least one start node")
if not self.get_leaf_nodes():
raise ValueError("Graph must have at least one leaf node")
# Outgoing edge condition validation (per node)
for node in self.nodes.values():
# Check that if a node has an outgoing conditional edge, then all outgoing edges are conditional
has_condition = any(
edge.condition is not None or edge.condition_function is not None for edge in node.edges
)
has_unconditioned = any(edge.condition is None and edge.condition_function is None for edge in node.edges)
if has_condition and has_unconditioned:
raise ValueError(f"Node '{node.name}' has a mix of conditional and unconditional edges.")
# Validate activation conditions across all edges in the graph
self._validate_activation_conditions()
self._has_cycles = self.has_cycles_with_exit()
def _validate_activation_conditions(self) -> None:
"""Validate that all edges pointing to the same target node have consistent activation_condition values.View on GitHub (pinned to 027ecf0a37)
Solutions
- Add a terminal node with no outgoing edges that the workflow reaches (e.g. via a conditional edge 'approved' -> finalizer).
- Remove unintended outgoing edges from the node meant to be the last step.
- Verify get_leaf_nodes() non-empty in tests before constructing GraphFlow.
Example fix
// before builder.add_edge(reviewer, reviser, condition="needs work") builder.add_edge(reviser, reviewer) # no leaf // after builder.add_edge(reviewer, reviser, condition="needs work") builder.add_edge(reviser, reviewer, condition_function=lambda m: not _done(m)) builder.add_edge(reviewer, finalizer, condition="done") # finalizer is a leaf
Defensive patterns
Strategy: validation
Validate before calling
graph = builder.build()
if not graph.get_leaf_nodes():
raise ValueError("No leaf node: add a terminal node with no outgoing edges")
flow = GraphFlow(participants, graph_builder=builder) Type guard
def has_leaf_node(out_degree: dict[str, int]) -> bool:
return any(count == 0 for count in out_degree.values()) Try / catch
try:
flow = GraphFlow(participants, graph_builder=builder)
except ValueError as e:
if "at least one leaf node" in str(e):
# add a terminal node reachable via a conditional exit edge
... Prevention
- End every workflow with an explicit finalizer/reporter node that has no outgoing edges.
- Double-check that 'final' nodes never gain outgoing edges during refactors.
- Test graph structure (start + leaf) separately from runtime behavior.
When it happens
Trigger: Every node participates in an edge (e.g. A->B->C->A); a 'finalizer' node accidentally given an outgoing edge via add_edge(finalizer, other); dynamically generated graphs where the terminal step was dropped.
Common situations: Loops without an exit branch; refactoring that adds a 'logging' edge from the last node; config-driven graphs missing the terminal node definition.
Related errors
- Cycle detected without exit condition: {' -> '.join(cycle_no
- Graph must have at least one start node
- All agents in the workflow must be in the group chat.
- Graph has no nodes.
- Node '{node.name}' has a mix of conditional and unconditiona
AI-assisted analysis of microsoft/autogen@027ecf0a37 (2026-08-15).
Data as JSON: /api/errors/f5e8358776fda15f.
Report an issue: GitHub.