invoke-ai/InvokeAI · error · CyclicalGraphError
Graph contains cycles
Error message
Graph contains cycles
What it means
CyclicalGraphError raised by GraphExecution.validate_self() when the flattened networkx graph fails nx.is_directed_acyclic_graph(). InvokeAI graphs must be DAGs because execution order is a topological walk; a cycle means nodes feed each other's inputs with no valid start point. Thrown during graph validation before any node is executed.
Source
Thrown at invokeai/app/services/shared/graph.py:1833
if edge.source.field not in source_node.get_output_annotation().model_fields:
raise NodeFieldNotFoundError(
f"Edge source field {edge.source.field} does not exist in node {edge.source.node_id}"
)
if edge.destination.field not in type(destination_node).model_fields:
if isinstance(destination_node, CallSavedWorkflowInvocation) and is_call_saved_workflow_dynamic_input(
edge.destination.field
):
continue
raise NodeFieldNotFoundError(
f"Edge destination field {edge.destination.field} does not exist in node {edge.destination.node_id}"
)
def _validate_graph_is_acyclic(self) -> None:
graph = self.nx_graph_flat()
if not nx.is_directed_acyclic_graph(graph):
raise CyclicalGraphError("Graph contains cycles")
def _validate_edge_type_compatibility(self) -> None:
for edge in self.edges:
destination_node = self.get_node(edge.destination.node_id)
if isinstance(destination_node, CallSavedWorkflowInvocation) and is_call_saved_workflow_dynamic_input(
edge.destination.field
):
continue
if not are_connections_compatible(
self.get_node(edge.source.node_id),
edge.source.field,
destination_node,
edge.destination.field,
):
raise InvalidEdgeError(f"Edge source and target types do not match ({edge})")
def _validate_special_nodes(self) -> None:
# TODO: may need to validate all iterators & collectors in subgraphs so edge connections in parent graphs will be availableView on GitHub (pinned to 0b6a024f2f)
Solutions
- Inspect graph.nx_graph_flat() with networkx.find_cycle() to identify the offending node loop
- Remove or redirect one edge in the cycle so data flows in a single direction
- If the intent was feedback, restructure with CollectInvocation/IterateInvocation nodes or split the node
- Validate programmatically with validate_self() before saving or queueing workflows
Example fix
// before g.add_edge(a, "image", b, "image") g.add_edge(b, "image", a, "image") # cycle // after g.add_edge(a, "image", b, "image") g.add_edge(b, "image", c, "image") # acyclic flow
Defensive patterns
Strategy: validation
Validate before calling
import networkx as nx
def graph_is_acyclic(graph):
return nx.is_directed_acyclic_graph(graph.nx_graph_flat())
if not graph_is_acyclic(g):
cycle = nx.find_cycle(g.nx_graph_flat())
raise ValueError(f"Fix cycle before use: {cycle}") Type guard
def is_acyclic_graph(graph) -> bool:
try:
return nx.is_directed_acyclic_graph(graph.nx_graph_flat())
except Exception:
return False Try / catch
from invokeai.app.services.shared.graph import CyclicalGraphError
try:
graph.validate_self()
except CyclicalGraphError as e:
logger.error("cycle detected: %s", e)
# redirect or drop one edge in the cycle Prevention
- Call validate_self() before saving or queueing any programmatically built graph
- Never wire a node's output back into an ancestor node
- Use nx.find_cycle() in tests over all stored workflow JSONs
When it happens
Trigger: Adding edges that form a closed loop among existing nodes, then calling graph.validate_self(); deserializing/storing a workflow JSON that contains a cycle (e.g. node A output -> node B input and node B output -> node A input).
Common situations: Hand-edited workflow JSON files, programmatic graph construction with an index/order bug connecting nodes, copy-pasting nodes in the UI and rewiring backwards, or an older workflow schema migrated incorrectly.
Related errors
- Edge creates a cycle in the graph ({edge})
- The selected saved workflow must contain exactly one workflo
- Edge source and target types do not match ({edge})
- Invalid iterator node ({node.id}): {err}
- Invalid collector node ({node.id}): {err}
AI-assisted analysis of invoke-ai/InvokeAI@0b6a024f2f (2026-08-29).
Data as JSON: /api/errors/60feec59056e9cc8.
Report an issue: GitHub.