GraphiteEditor/Graphite · error
Path should not be empty
Error message
Path should not be empty
What it means
shallowest_unique_layers builds each selected layer's ancestor path via layer.ancestors(metadata).collect(), sorts, dedups prefixes, then path.pop().expect("Path should not be empty"). A path is empty only when ancestors() yields nothing, which happens when the selected layer has no relations entry in DocumentMetadata (an orphaned/stale selection pointing at a node that is no longer in the layer tree). The expect then panics instead of skipping the stale entry.
Source
Thrown at editor/src/messages/portfolio/document/utility_types/network_interface/queries.rs:1039
let mut sorted_layers = if let Some(selected_nodes) = self.selected_nodes_in_nested_network(network_path) {
selected_nodes
.selected_layers(self.document_metadata())
.map(|layer| {
let mut layer_path = layer.ancestors(&self.document_metadata).collect::<Vec<_>>();
layer_path.reverse();
layer_path
})
.collect::<Vec<_>>()
} else {
log::error!("Could not get selected nodes in shallowest_unique_layers");
Vec::new()
};
// Sorting here creates groups of similar UUID paths
sorted_layers.sort();
sorted_layers.dedup_by(|a, b| a.starts_with(b));
sorted_layers.into_iter().map(|mut path| {
let layer = path.pop().expect("Path should not be empty");
assert!(
layer != LayerNodeIdentifier::ROOT_PARENT,
"The root parent cannot be selected, so it cannot be a shallowest selected layer"
);
layer
})
}
pub fn shallowest_unique_layers_sorted(&self, network_path: &[NodeId]) -> Vec<LayerNodeIdentifier> {
let all_layers_to_group = self.shallowest_unique_layers(network_path).collect::<Vec<_>>();
// Ensure nodes are grouped in the correct order
let mut all_layers_to_group_sorted = Vec::new();
for descendant in LayerNodeIdentifier::ROOT_PARENT.descendants(self.document_metadata()) {
if all_layers_to_group.contains(&descendant) {
all_layers_to_group_sorted.push(descendant);
};
}
all_layers_to_group_sortedView on GitHub (pinned to c507b35645)
Solutions
- Use filter_map(|mut path| path.pop()) so empty paths are skipped instead of panicking
- Clean the selection set whenever node deletion updates metadata, so ancestors() always resolves
- Log the offending identifier when a path is empty to identify which mutation left the stale selection
Example fix
// before
let layer = path.pop().expect("Path should not be empty");
// after
let Some(layer) = path.pop() else {
log::error!("Empty ancestor path in shallowest_unique_layers");
return LayerNodeIdentifier::ROOT_PARENT;
}; Defensive patterns
Strategy: validation
Validate before calling
// skip selections whose metadata has been removed before popping let selected: Vec<_> = selection.iter().filter(|layer| metadata.get_relations(layer).is_some()).collect();
Type guard
fn has_relations(layer: LayerNodeIdentifier, metadata: &DocumentMetadata) -> bool {
metadata.get_relations(layer).is_some()
} Prevention
- Clear selection entries whenever nodes are deleted from the document network
- Use filter_map on path.pop() so empty ancestor paths are skipped, not fatal
- Log stale identifiers to find the deletion path that leaves them selected
When it happens
Trigger: Selection contains a LayerNodeIdentifier whose relations were removed (deleted layer, collapsed network) while the selection set was not cleared, so its ancestor path is empty at the pop().
Common situations: Undo/redo races that delete nodes but keep them selected; custom tooling that inserts identifiers into the selection; document deserialization leaving selection IDs that no longer map to metadata.
Related errors
- Ungrouped folder must have a parent
- Parent accessed via child should have children
- Solidify Stroke node should exist
- In `check_layer()`: there should be a `target`
- Artboard should have a primary input
AI-assisted analysis of GraphiteEditor/Graphite@c507b35645 (2026-08-16).
Data as JSON: /api/errors/fdecd586d722e3f7.
Report an issue: GitHub.