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

  1. Pass a termination_condition such as MaxMessageTermination(n) or TextMentionTermination("APPROVED") to GraphFlow.
  2. Or set max_turns=N to hard-cap the number of turns.
  3. 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

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


AI-assisted analysis of microsoft/autogen@027ecf0a37 (2026-08-15). Data as JSON: /api/errors/ed12dce2c6e9ba70. Report an issue: GitHub.