{"record":{"id":"60feec59056e9cc8","repo":"invoke-ai/InvokeAI","slug":"graph-contains-cycles","errorCode":null,"errorMessage":"Graph contains cycles","messagePattern":"Graph contains cycles","errorType":"validation","errorClass":"CyclicalGraphError","httpStatus":null,"severity":"error","filePath":"invokeai/app/services/shared/graph.py","lineNumber":1833,"sourceCode":"\n            if edge.source.field not in source_node.get_output_annotation().model_fields:\n                raise NodeFieldNotFoundError(\n                    f\"Edge source field {edge.source.field} does not exist in node {edge.source.node_id}\"\n                )\n\n            if edge.destination.field not in type(destination_node).model_fields:\n                if isinstance(destination_node, CallSavedWorkflowInvocation) and is_call_saved_workflow_dynamic_input(\n                    edge.destination.field\n                ):\n                    continue\n                raise NodeFieldNotFoundError(\n                    f\"Edge destination field {edge.destination.field} does not exist in node {edge.destination.node_id}\"\n                )\n\n    def _validate_graph_is_acyclic(self) -> None:\n        graph = self.nx_graph_flat()\n        if not nx.is_directed_acyclic_graph(graph):\n            raise CyclicalGraphError(\"Graph contains cycles\")\n\n    def _validate_edge_type_compatibility(self) -> None:\n        for edge in self.edges:\n            destination_node = self.get_node(edge.destination.node_id)\n            if isinstance(destination_node, CallSavedWorkflowInvocation) and is_call_saved_workflow_dynamic_input(\n                edge.destination.field\n            ):\n                continue\n            if not are_connections_compatible(\n                self.get_node(edge.source.node_id),\n                edge.source.field,\n                destination_node,\n                edge.destination.field,\n            ):\n                raise InvalidEdgeError(f\"Edge source and target types do not match ({edge})\")\n\n    def _validate_special_nodes(self) -> None:\n        # TODO: may need to validate all iterators & collectors in subgraphs so edge connections in parent graphs will be available","sourceCodeStart":1815,"sourceCodeEnd":1851,"githubUrl":"https://github.com/invoke-ai/InvokeAI/blob/0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06/invokeai/app/services/shared/graph.py#L1815-L1851","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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"],"exampleFix":"// before\ng.add_edge(a, \"image\", b, \"image\")\ng.add_edge(b, \"image\", a, \"image\")  # cycle\n// after\ng.add_edge(a, \"image\", b, \"image\")\ng.add_edge(b, \"image\", c, \"image\")  # acyclic flow","handlingStrategy":"validation","validationCode":"import networkx as nx\n\ndef graph_is_acyclic(graph):\n    return nx.is_directed_acyclic_graph(graph.nx_graph_flat())\n\nif not graph_is_acyclic(g):\n    cycle = nx.find_cycle(g.nx_graph_flat())\n    raise ValueError(f\"Fix cycle before use: {cycle}\")","typeGuard":"def is_acyclic_graph(graph) -> bool:\n    try:\n        return nx.is_directed_acyclic_graph(graph.nx_graph_flat())\n    except Exception:\n        return False","tryCatchPattern":"from invokeai.app.services.shared.graph import CyclicalGraphError\n\ntry:\n    graph.validate_self()\nexcept CyclicalGraphError as e:\n    logger.error(\"cycle detected: %s\", e)\n    # redirect or drop one edge in the cycle","preventionTips":["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"],"tags":["invokeai","graph-validation","cycle","dag"],"backgroundTag":"graph-contains-cycle","analyzedSha":"0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06","analyzedAt":"2026-08-29T04:46:49.967Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}