{"record":{"id":"132ae43f16fe9d4b","repo":"invoke-ai/InvokeAI","slug":"edge-already-exists-edge","errorCode":null,"errorMessage":"Edge already exists ({edge})","messagePattern":"Edge already exists \\((.+?)\\)","errorType":"validation","errorClass":"InvalidEdgeError","httpStatus":null,"severity":"error","filePath":"invokeai/app/services/shared/graph.py","lineNumber":1924,"sourceCode":"        \"\"\"Checks if the destination field for an edge is of type typing.Any\"\"\"\n        return get_input_field_type(self.get_node(edge.destination.node_id), edge.destination.field) == Any\n\n    def _is_destination_field_list_of_Any(self, edge: Edge) -> bool:\n        \"\"\"Checks if the destination field for an edge is of type typing.Any\"\"\"\n        return get_input_field_type(self.get_node(edge.destination.node_id), edge.destination.field) == list[Any]\n\n    def _get_edge_nodes(self, edge: Edge) -> tuple[BaseInvocation, BaseInvocation]:\n        try:\n            return self.get_node(edge.source.node_id), self.get_node(edge.destination.node_id)\n        except NodeNotFoundError:\n            raise InvalidEdgeError(f\"One or both nodes don't exist ({edge})\")\n\n    def _validate_edge_destination_uniqueness(self, edge: Edge, destination_node: BaseInvocation) -> None:\n        input_edges = self._get_input_edges(edge.destination.node_id, edge.destination.field)\n        if len(input_edges) > 0 and (\n            not isinstance(destination_node, CollectInvocation) or edge.destination.field != ITEM_FIELD\n        ):\n            raise InvalidEdgeError(f\"Edge already exists ({edge})\")\n\n    def _validate_edge_would_not_create_cycle(self, edge: Edge) -> None:\n        graph = self.nx_graph_flat()\n        graph.add_edge(edge.source.node_id, edge.destination.node_id)\n        if not nx.is_directed_acyclic_graph(graph):\n            raise InvalidEdgeError(f\"Edge creates a cycle in the graph ({edge})\")\n\n    def _validate_edge_field_compatibility(\n        self, edge: Edge, source_node: BaseInvocation, destination_node: BaseInvocation\n    ) -> None:\n        if isinstance(destination_node, CallSavedWorkflowInvocation) and is_call_saved_workflow_dynamic_input(\n            edge.destination.field\n        ):\n            return\n        if not are_connections_compatible(source_node, edge.source.field, destination_node, edge.destination.field):\n            raise InvalidEdgeError(f\"Field types are incompatible ({edge})\")\n\n    def _validate_iterator_edge_rules(","sourceCodeStart":1906,"sourceCodeEnd":1942,"githubUrl":"https://github.com/invoke-ai/InvokeAI/blob/0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06/invokeai/app/services/shared/graph.py#L1906-L1942","documentation":"InvalidEdgeError raised by _validate_edge_destination_uniqueness() when a destination input field already has an incoming edge. Most input fields accept exactly one edge; only CollectInvocation's 'item' field may receive many. This prevents silently overwriting or duplicating a node's input.","triggerScenarios":"Calling g.add_edge() connecting a second edge into the same (node_id, field) destination that is not a CollectInvocation item field.","commonSituations":"Re-running graph-building scripts that add edges twice, wiring two outputs into a single non-collect input, UI/script mix-ups after editing an existing workflow.","solutions":["Remove the existing edge into that field before adding the new one","If you need to merge multiple outputs, route them through a CollectInvocation item field","Make graph construction idempotent (check for the edge before adding)","In the UI, delete the old connection first when rewiring"],"exampleFix":"// before\ng.add_edge(a, \"image\", denoise, \"image\")\ng.add_edge(b, \"image\", denoise, \"image\")  # duplicate destination\n// after\ng.add_edge(a, \"image\", denoise, \"image\")\ng.delete_edge(a, \"image\", denoise, \"image\")\ng.add_edge(b, \"image\", denoise, \"image\")","handlingStrategy":"validation","validationCode":"def has_input_edge(graph, node_id: str, field: str) -> bool:\n    return any(\n        e.destination.node_id == node_id and e.destination.field == field\n        for e in graph.edges\n    )\n\nif not has_input_edge(g, dst_id, field):\n    g.add_edge(src_id, src_field, dst_id, field)","typeGuard":"def destination_is_free(graph, edge) -> bool:\n    return not any(\n        e.destination.node_id == edge.destination.node_id\n        and e.destination.field == edge.destination.field\n        for e in graph.edges\n    )","tryCatchPattern":"from invokeai.app.services.shared.graph import InvalidEdgeError\n\ntry:\n    g.add_edge(src, \"image\", dst, \"image\")\nexcept InvalidEdgeError as e:\n    if \"already exists\" in str(e):\n        g.delete_edge(src, \"image\", dst, \"image\")\n        g.add_edge(src, \"image\", dst, \"image\")  # replace","preventionTips":["Make graph-building scripts idempotent: check for existing edges before adding","Delete the old connection before rewiring an input field","Use a CollectInvocation when multiple sources must feed one consumer"],"tags":["invokeai","graph-validation","duplicate-edge","edges"],"backgroundTag":"duplicate-edge","analyzedSha":"0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06","analyzedAt":"2026-08-29T04:46:49.967Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}