{"record":{"id":"792e3969554a94c3","repo":"GraphiteEditor/Graphite","slug":"encountered-invalid-node-id","errorCode":null,"errorMessage":"Encountered invalid node id","messagePattern":"Encountered invalid node id","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"node-graph/graph-craft/src/document.rs","lineNumber":833,"sourceCode":"\t\t\t\tnode.original_location.dependants = (0..node.implementation.output_count()).map(|_| Vec::new()).collect();\n\t\t\t}\n\t\t}\n\t}\n\n\tpub fn populate_dependants(&mut self) {\n\t\tlet mut dep_changes = Vec::new();\n\t\tfor (node_id, node) in &mut self.nodes {\n\t\t\tlet len = node.original_location.dependants.len();\n\t\t\tnode.original_location.dependants.extend(vec![vec![]; (node.implementation.output_count()).max(len) - len]);\n\t\t\tfor input in &node.inputs {\n\t\t\t\tif let NodeInput::Node { node_id: dep_id, output_index, .. } = input {\n\t\t\t\t\tdep_changes.push((*dep_id, *output_index, *node_id));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// println!(\"{:#?}\", self.nodes.get(&NodeId(1)));\n\t\tfor (dep_id, output_index, node_id) in dep_changes {\n\t\t\tlet node = self.nodes.get_mut(&dep_id).expect(\"Encountered invalid node id\");\n\t\t\tlet len = node.original_location.dependants.len();\n\t\t\t// One must be added to the index to find the length because indexing in rust starts from 0.\n\t\t\tnode.original_location.dependants.extend(vec![vec![]; (output_index + 1).max(len) - len]);\n\t\t\t// println!(\"{node_id} {output_index} {}\", node.implementation.output_count());\n\t\t\tnode.original_location.dependants[output_index].push(node_id);\n\t\t}\n\t}\n\n\t/// Replace all references in any node of `old_input` with `new_input`\n\tfn replace_node_inputs(&mut self, node_id: NodeId, old_input: (NodeId, usize), new_input: (NodeId, usize)) {\n\t\tlet Some(node) = self.nodes.get_mut(&node_id) else { return };\n\t\tnode.inputs.iter_mut().for_each(|input| {\n\t\t\tif let NodeInput::Node { node_id: input_id, output_index, .. } = input\n\t\t\t\t&& (*input_id, *output_index) == old_input\n\t\t\t{\n\t\t\t\t(*input_id, *output_index) = new_input;\n\t\t\t}\n\t\t});","sourceCodeStart":815,"sourceCodeEnd":851,"githubUrl":"https://github.com/GraphiteEditor/Graphite/blob/c507b356453361e31638b8bff8f6d46b6da2961e/node-graph/graph-craft/src/document.rs#L815-L851","documentation":"While regenerating dependant lists after a network is loaded, this code records every NodeInput::Node reference and then looks up each referenced node with nodes.get_mut(&dep_id). This panic means some input points at a node id that is not present in self.nodes — the graph contains a dangling node reference.","triggerScenarios":"Documents whose serialized network references a node id that was deleted without rewriting inputs pointing at it; programmatic graph edits (macros, CLI transforms) that remove nodes but leave inputs behind; truncated or hand-edited .graphite/.gdd files.","commonSituations":"Opening third-party or hand-edited artwork; files saved by editor versions with node-removal bugs; test fixtures built by serializing partial networks.","solutions":["Re-open the file in the editor version that produced it and re-save, which normalizes the references","Pre-validate before this pass: collect every NodeInput node id and confirm each exists in nodes","When editing graphs in code, route deletions through APIs that remove or rebind referencing inputs instead of mutating the map directly"],"exampleFix":"// before\nlet node = self.nodes.get_mut(&dep_id).expect(\"Encountered invalid node id\");\n\n// after\nlet Some(node) = self.nodes.get_mut(&dep_id) else {\n\tlog::warn!(\"skipping dangling reference to node {dep_id}\");\n\tcontinue;\n};","handlingStrategy":"validation","validationCode":"fn dangling_references(network: &NodeNetwork) -> Vec<NodeId> {\n\tlet mut missing = Vec::new();\n\tfor node in network.nodes.values() {\n\t\tfor input in &node.inputs {\n\t\t\tif let NodeInput::Node { node_id: dep_id, .. } = input {\n\t\t\t\tif !network.nodes.contains_key(dep_id) {\n\t\t\t\t\tmissing.push(*dep_id);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tmissing\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always delete nodes through editor APIs that rebind or remove referencing inputs","Validate loaded documents for dangling references before evaluation","Keep backups of artwork so a corrupted graph can be restored from a prior save"],"tags":["node-graph","document","dangling-reference","graphite"],"backgroundTag":"dangling-node-reference","analyzedSha":"c507b356453361e31638b8bff8f6d46b6da2961e","analyzedAt":"2026-08-16T21:57:18.596Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}