GraphiteEditor/Graphite · error
Encountered invalid node id
Error message
Encountered invalid node id
What it means
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.
Source
Thrown at node-graph/graph-craft/src/document.rs:833
node.original_location.dependants = (0..node.implementation.output_count()).map(|_| Vec::new()).collect();
}
}
}
pub fn populate_dependants(&mut self) {
let mut dep_changes = Vec::new();
for (node_id, node) in &mut self.nodes {
let len = node.original_location.dependants.len();
node.original_location.dependants.extend(vec![vec![]; (node.implementation.output_count()).max(len) - len]);
for input in &node.inputs {
if let NodeInput::Node { node_id: dep_id, output_index, .. } = input {
dep_changes.push((*dep_id, *output_index, *node_id));
}
}
}
// println!("{:#?}", self.nodes.get(&NodeId(1)));
for (dep_id, output_index, node_id) in dep_changes {
let node = self.nodes.get_mut(&dep_id).expect("Encountered invalid node id");
let len = node.original_location.dependants.len();
// One must be added to the index to find the length because indexing in rust starts from 0.
node.original_location.dependants.extend(vec![vec![]; (output_index + 1).max(len) - len]);
// println!("{node_id} {output_index} {}", node.implementation.output_count());
node.original_location.dependants[output_index].push(node_id);
}
}
/// Replace all references in any node of `old_input` with `new_input`
fn replace_node_inputs(&mut self, node_id: NodeId, old_input: (NodeId, usize), new_input: (NodeId, usize)) {
let Some(node) = self.nodes.get_mut(&node_id) else { return };
node.inputs.iter_mut().for_each(|input| {
if let NodeInput::Node { node_id: input_id, output_index, .. } = input
&& (*input_id, *output_index) == old_input
{
(*input_id, *output_index) = new_input;
}
});View on GitHub (pinned to c507b35645)
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
Example fix
// before
let node = self.nodes.get_mut(&dep_id).expect("Encountered invalid node id");
// after
let Some(node) = self.nodes.get_mut(&dep_id) else {
log::warn!("skipping dangling reference to node {dep_id}");
continue;
}; Defensive patterns
Strategy: validation
Validate before calling
fn dangling_references(network: &NodeNetwork) -> Vec<NodeId> {
let mut missing = Vec::new();
for node in network.nodes.values() {
for input in &node.inputs {
if let NodeInput::Node { node_id: dep_id, .. } = input {
if !network.nodes.contains_key(dep_id) {
missing.push(*dep_id);
}
}
}
}
missing
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: Opening third-party or hand-edited artwork; files saved by editor versions with node-removal bugs; test fixtures built by serializing partial networks.
Related errors
- node not found in lookup table
- Failed to parse document
- Failed to expand network
- ApplicationIo must be available when using resources
- ApplicationIo not available
AI-assisted analysis of GraphiteEditor/Graphite@c507b35645 (2026-08-16).
Data as JSON: /api/errors/792e3969554a94c3.
Report an issue: GitHub.