invoke-ai/InvokeAI · error · DuplicateNodeIdError
Node ids must be unique, found duplicates {duplicate_node_id
Error message
Node ids must be unique, found duplicates {duplicate_node_ids} What it means
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).
Source
Thrown at invokeai/app/services/shared/graph.py:1799
list.extend(self.edges, new_edges)
for edge in new_edges:
self._add_edge_to_indexes(edge)
def delete_edge(self, edge: Edge) -> None:
"""Deletes an edge from a graph"""
try:
list.remove(self.edges, edge)
self._remove_edge_from_indexes(edge)
except ValueError:
pass
def _validate_unique_node_ids(self) -> None:
node_ids = [n.id for n in self.nodes.values()]
seen = set()
duplicate_node_ids = {nid for nid in node_ids if (nid in seen) or seen.add(nid)}
if duplicate_node_ids:
raise DuplicateNodeIdError(f"Node ids must be unique, found duplicates {duplicate_node_ids}")
def _validate_node_id_mapping(self) -> None:
for node_dict_id, node in self.nodes.items():
if node_dict_id != node.id:
raise NodeIdMismatchError(f"Node ids must match, got {node_dict_id} and {node.id}")
def _validate_edge_nodes_and_fields(self) -> None:
for edge in self.edges:
source_node = self.nodes.get(edge.source.node_id, None)
if source_node is None:
raise NodeNotFoundError(f"Edge source node {edge.source.node_id} does not exist in the graph")
destination_node = self.nodes.get(edge.destination.node_id, None)
if destination_node is None:
raise NodeNotFoundError(f"Edge destination node {edge.destination.node_id} does not exist in the graph")
if edge.source.field not in source_node.get_output_annotation().model_fields:
raise NodeFieldNotFoundError(View on GitHub (pinned to 0b6a024f2f)
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.
Example fix
// before
graph = Graph(nodes={"n1": node_a, "n1": node_b}) # DuplicateNodeIdError on validate
// after
import uuid
for n in merged_nodes:
n.id = uuid.uuid4().hex # remap edges referencing old ids accordingly
graph.add_node(n) Defensive patterns
Strategy: validation
Validate before calling
def find_duplicate_node_ids(nodes) -> set:
seen, dups = set(), set()
for n in nodes:
(dups if n.id in seen else seen).add(n.id)
return dups
dups = find_duplicate_node_ids(nodes)
if dups:
raise ValueError(f"fix duplicate ids before validating graph: {dups}") Type guard
def graph_has_unique_node_ids(graph) -> bool:
ids = [n.id for n in graph.nodes.values()]
return len(ids) == len(set(ids)) Try / catch
try:
graph.validate_self()
except DuplicateNodeIdError as e:
print(e) # 'Node ids must be unique, found duplicates {...}'
rename_duplicate_nodes(graph) # assign fresh uuid4 ids + remap edges
graph.validate_self() Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- The selected saved workflow must not contain more than one w
- Node {batch_data.node_path} not found in graph
- Field {batch_data.field_name} not found in node {batch_data.
- Node ids must match, got {node_dict_id} and {node.id}
- Edge source node {edge.source.node_id} does not exist in the
AI-assisted analysis of invoke-ai/InvokeAI@0b6a024f2f (2026-08-29).
Data as JSON: /api/errors/c2cc2ffbf3ab6527.
Report an issue: GitHub.