microsoft/autogen · error · ValueError
A termination condition is required for cyclic graphs withou
Error message
A termination condition is required for cyclic graphs without a maximum turn limit.
What it means
Raised when constructing a graph-based flow whose graph contains a (valid, conditional) cycle but the team has neither a termination_condition nor max_turns. A cyclic graph can run forever; the library requires an explicit stopping mechanism — a TerminationCondition or a turn cap — before it will run the loop.
Source
Thrown at python/packages/autogen-agentchat/src/autogen_agentchat/teams/_group_chat/_graph/_digraph_group_chat.py:341
message_factory: MessageFactory,
graph: DiGraph,
) -> None:
"""Initialize the graph-based execution manager."""
super().__init__(
name=name,
group_topic_type=group_topic_type,
output_topic_type=output_topic_type,
participant_topic_types=participant_topic_types,
participant_names=participant_names,
participant_descriptions=participant_descriptions,
output_message_queue=output_message_queue,
termination_condition=termination_condition,
max_turns=max_turns,
message_factory=message_factory,
)
graph.graph_validate()
if graph.get_has_cycles() and self._termination_condition is None and self._max_turns is None:
raise ValueError("A termination condition is required for cyclic graphs without a maximum turn limit.")
self._graph = graph
# Lookup table for incoming edges for each node.
self._parents = graph.get_parents()
# Lookup table for outgoing edges for each node.
self._edges: Dict[str, List[DiGraphEdge]] = {n: node.edges for n, node in graph.nodes.items()}
# Build activation and enqueued_any lookup tables by collecting all edges and grouping by target node
self._build_lookup_tables(graph)
# Track which activation groups were triggered for each node
self._triggered_activation_groups: Dict[str, Set[str]] = {}
# === Mutable states for the graph execution ===
# Count the number of remaining parents to activate each node.
self._remaining: Dict[str, Counter[str]] = {
target: Counter(groups) for target, groups in graph.get_remaining_map().items()
}
# cache for remaining
self._origin_remaining: Dict[str, Dict[str, int]] = {View on GitHub (pinned to 027ecf0a37)
Solutions
- Pass a termination_condition such as MaxMessageTermination(n) or TextMentionTermination("APPROVED") to GraphFlow.
- Or set max_turns=N to hard-cap the number of turns.
- Prefer both: a semantic termination condition plus a generous max_turns safety net.
Example fix
// before
flow = GraphFlow([draft, review], graph_builder=builder) # graph has a cycle
// after
from autogen_agentchat.conditions import MaxMessageTermination, TextMentionTermination
flow = GraphFlow([draft, review], graph_builder=builder,
termination_condition=TextMentionTermination("APPROVED") | MaxMessageTermination(20)) Defensive patterns
Strategy: validation
Validate before calling
graph = builder.build()
if graph.get_has_cycles() and termination_condition is None and max_turns is None:
raise ValueError("Cyclic graph needs termination_condition or max_turns")
flow = GraphFlow(participants, graph_builder=builder,
termination_condition=termination_condition, max_turns=max_turns) Type guard
def flow_has_stop_criterion(has_cycles: bool, termination_condition, max_turns) -> bool:
return not has_cycles or termination_condition is not None or max_turns is not None Try / catch
try:
flow = GraphFlow(participants, graph_builder=builder)
except ValueError as e:
if "termination condition is required" in str(e):
# add termination_condition=TextMentionTermination('APPROVED') or max_turns=N
... Prevention
- Always pair cyclic graphs with a semantic termination condition plus a max_turns cap.
- Choose termination phrases the agents can actually emit (and document them in prompts).
- Treat a missing stop criterion as a build error in CI for graph workflows.
When it happens
Trigger: GraphFlow(participants, ...) with a conditional loop in the graph and no termination_condition= and no max_turns= arguments; removing a termination condition during debugging; porting a linear graph to cyclic without adding stopping criteria.
Common situations: Draft/review/revise loops; teams built from config where the termination section is optional and omitted; interactive workflows where the author assumed the loop's conditions alone would stop it.
Related errors
- Cycle detected without exit condition: {' -> '.join(cycle_no
- Graph has no nodes.
- Source node '{source_name}' must be added before adding an e
- Target node '{target_name}' must be added before adding an e
- Failed to get login URL
AI-assisted analysis of microsoft/autogen@027ecf0a37 (2026-08-15).
Data as JSON: /api/errors/ed12dce2c6e9ba70.
Report an issue: GitHub.