{"record":{"id":"c2cc2ffbf3ab6527","repo":"invoke-ai/InvokeAI","slug":"node-ids-must-be-unique-found-duplicates-duplica","errorCode":null,"errorMessage":"Node ids must be unique, found duplicates {duplicate_node_ids}","messagePattern":"Node ids must be unique, found duplicates (.+?)","errorType":"validation","errorClass":"DuplicateNodeIdError","httpStatus":null,"severity":"error","filePath":"invokeai/app/services/shared/graph.py","lineNumber":1799,"sourceCode":"        list.extend(self.edges, new_edges)\n        for edge in new_edges:\n            self._add_edge_to_indexes(edge)\n\n    def delete_edge(self, edge: Edge) -> None:\n        \"\"\"Deletes an edge from a graph\"\"\"\n\n        try:\n            list.remove(self.edges, edge)\n            self._remove_edge_from_indexes(edge)\n        except ValueError:\n            pass\n\n    def _validate_unique_node_ids(self) -> None:\n        node_ids = [n.id for n in self.nodes.values()]\n        seen = set()\n        duplicate_node_ids = {nid for nid in node_ids if (nid in seen) or seen.add(nid)}\n        if duplicate_node_ids:\n            raise DuplicateNodeIdError(f\"Node ids must be unique, found duplicates {duplicate_node_ids}\")\n\n    def _validate_node_id_mapping(self) -> None:\n        for node_dict_id, node in self.nodes.items():\n            if node_dict_id != node.id:\n                raise NodeIdMismatchError(f\"Node ids must match, got {node_dict_id} and {node.id}\")\n\n    def _validate_edge_nodes_and_fields(self) -> None:\n        for edge in self.edges:\n            source_node = self.nodes.get(edge.source.node_id, None)\n            if source_node is None:\n                raise NodeNotFoundError(f\"Edge source node {edge.source.node_id} does not exist in the graph\")\n\n            destination_node = self.nodes.get(edge.destination.node_id, None)\n            if destination_node is None:\n                raise NodeNotFoundError(f\"Edge destination node {edge.destination.node_id} does not exist in the graph\")\n\n            if edge.source.field not in source_node.get_output_annotation().model_fields:\n                raise NodeFieldNotFoundError(","sourceCodeStart":1781,"sourceCodeEnd":1817,"githubUrl":"https://github.com/invoke-ai/InvokeAI/blob/0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06/invokeai/app/services/shared/graph.py#L1781-L1817","documentation":"DuplicateNodeIdError (a ValueError subclass) is raised by Graph._validate_unique_node_ids when two or more node entries share the same node.id during graph validation. Unlike NodeAlreadyInGraphError (raised at add_node time), this is a bulk validation that runs when a graph is validated/loaded as a whole, catching duplicates introduced outside add_node (e.g., via direct dict manipulation or deserialization).","triggerScenarios":"Validating or instantiating a Graph whose nodes dict (or incoming node list) contains two nodes with equal id values; loading a workflow JSON where copy-paste produced identical node ids; deserializing a graph serialized from concatenated node lists.","commonSituations":"Hand-edited workflow JSON where a node was duplicated without changing its id; merging saved graphs; scripts that build a raw nodes dict and construct Graph(nodes={...}) bypassing add_node's uniqueness check; version changes where validation now runs on graphs that previously loaded laxly.","solutions":["Open the workflow JSON and give each duplicated node a unique id (also updating edge references to it).","Rebuild the graph programmatically via add_node with fresh uuid4 ids instead of constructing the raw dict.","Before validation, run your own dedup: build an id set and rename or drop duplicates plus fix their edges.","If duplicates come from template merging, regenerate ids for the merged-in subgraph nodes and remap edges."],"exampleFix":"// before\ngraph = Graph(nodes={\"n1\": node_a, \"n1\": node_b})  # DuplicateNodeIdError on validate\n// after\nimport uuid\nfor n in merged_nodes:\n    n.id = uuid.uuid4().hex  # remap edges referencing old ids accordingly\n    graph.add_node(n)","handlingStrategy":"validation","validationCode":"def find_duplicate_node_ids(nodes) -> set:\n    seen, dups = set(), set()\n    for n in nodes:\n        (dups if n.id in seen else seen).add(n.id)\n    return dups\n\ndups = find_duplicate_node_ids(nodes)\nif dups:\n    raise ValueError(f\"fix duplicate ids before validating graph: {dups}\")","typeGuard":"def graph_has_unique_node_ids(graph) -> bool:\n    ids = [n.id for n in graph.nodes.values()]\n    return len(ids) == len(set(ids))","tryCatchPattern":"try:\n    graph.validate_self()\nexcept DuplicateNodeIdError as e:\n    print(e)  # 'Node ids must be unique, found duplicates {...}'\n    rename_duplicate_nodes(graph)  # assign fresh uuid4 ids + remap edges\n    graph.validate_self()","preventionTips":["Never hand-edit workflow JSON without changing duplicated node ids.","Construct graphs via add_node (which enforces uniqueness) rather than raw dicts.","Run graph_has_unique_node_ids before persisting or submitting a workflow.","After merging templates, regenerate ids for the merged-in nodes and remap their edges."],"tags":["graph","duplicate-id","validation"],"backgroundTag":"duplicate-node-id","analyzedSha":"0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06","analyzedAt":"2026-08-29T04:46:49.967Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}