microsoft/autogen · error · ValueError

Cycle detected without exit condition: {' -> '.join(cycle_no

Error message

Cycle detected without exit condition: {' -> '.join(cycle_nodes + cycle_nodes[:1])}

What it means

Raised by DiGraph.has_cycles_with_exit during graph validation when a directed cycle is found in which every edge is unconditional (neither a textual condition nor a condition_function). An unconditional cycle has no way to exit, so the chat would loop forever; validation therefore rejects it. Cycles are allowed only when at least one edge in the cycle provides an exit condition.

Source

Thrown at python/packages/autogen-agentchat/src/autogen_agentchat/teams/_group_chat/_graph/_digraph_group_chat.py:183

            visited.add(node_name)
            rec_stack.add(node_name)
            path.append(node_name)
            cycle = False

            for edge in self.nodes[node_name].edges:
                target = edge.target
                if target not in visited:
                    if dfs(target):
                        cycle = True
                elif target in rec_stack:
                    # Found a cycle → extract the cycle
                    cycle_start_index = path.index(target)
                    cycle_nodes = path[cycle_start_index:]
                    cycle_edges: List[DiGraphEdge] = []
                    for n in cycle_nodes:
                        cycle_edges.extend(self.nodes[n].edges)
                    if all(edge.condition is None and edge.condition_function is None for edge in cycle_edges):
                        raise ValueError(
                            f"Cycle detected without exit condition: {' -> '.join(cycle_nodes + cycle_nodes[:1])}"
                        )
                    cycle = True  # Found cycle, but it has an exit condition

            rec_stack.remove(node_name)
            path.pop()
            return cycle

        has_cycle = False
        for node in self.nodes:
            if node not in visited:
                if dfs(node):
                    has_cycle = True

        return has_cycle

    def get_has_cycles(self) -> bool:
        """Indicates if the graph has at least one cycle (with valid exit conditions)."""

View on GitHub (pinned to 027ecf0a37)

Solutions

  1. Add condition="..." or condition_function=... to at least one edge in the cycle (typically the back-edge), describing when to leave the loop.
  2. Alternatively break the cycle with a conditional edge to an exit/leaf node.
  3. If the loop is intentionally bounded, also consider max_turns on the team as a safety net (though the structural fix is still required).

Example fix

// before
builder.add_edge(reviewer, reviser)
builder.add_edge(reviser, reviewer)  # unconditional cycle -> error

// after
builder.add_edge(reviewer, reviser, condition="revision needed")
builder.add_edge(reviser, reviewer, condition_function=lambda m: "approved" not in str(m).lower())
builder.add_edge(reviewer, finalizer, condition="approved")
Defensive patterns

Strategy: validation

Validate before calling

def cycle_edges_have_exit(builder: DiGraphBuilder) -> bool:
    try:
        builder.build().graph_validate()
        return True
    except ValueError as e:
        if "Cycle detected without exit condition" in str(e):
            return False
        raise

Type guard

def edge_is_conditional(edge) -> bool:
    return edge.condition is not None or edge.condition_function is not None

Try / catch

try:
    flow = GraphFlow(participants, graph_builder=builder)
except ValueError as e:
    if "Cycle detected without exit condition" in str(e):
        # add condition/condition_function to at least one edge in the reported cycle
        ...

Prevention

When it happens

Trigger: Using DiGraphBuilder.add_edge in a loop (e.g. reviewer -> reviser -> reviewer) without condition= or condition_function= on any edge of the cycle; converting a linear workflow to a cyclic one while forgetting to add the exit condition; typos making a condition evaluate falsy at build time is not the issue here — the check is purely structural (condition is None).

Common situations: Draft/review/revise loops in GraphFlow; porting workflows from other frameworks where loops are bounded externally; adding a 'back edge' for retries without specifying when to stop.

Related errors


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