{"record":{"id":"fdecd586d722e3f7","repo":"GraphiteEditor/Graphite","slug":"path-should-not-be-empty","errorCode":null,"errorMessage":"Path should not be empty","messagePattern":"Path should not be empty","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"editor/src/messages/portfolio/document/utility_types/network_interface/queries.rs","lineNumber":1039,"sourceCode":"\t\tlet mut sorted_layers = if let Some(selected_nodes) = self.selected_nodes_in_nested_network(network_path) {\n\t\t\tselected_nodes\n\t\t\t\t.selected_layers(self.document_metadata())\n\t\t\t\t.map(|layer| {\n\t\t\t\t\tlet mut layer_path = layer.ancestors(&self.document_metadata).collect::<Vec<_>>();\n\t\t\t\t\tlayer_path.reverse();\n\t\t\t\t\tlayer_path\n\t\t\t\t})\n\t\t\t\t.collect::<Vec<_>>()\n\t\t} else {\n\t\t\tlog::error!(\"Could not get selected nodes in shallowest_unique_layers\");\n\t\t\tVec::new()\n\t\t};\n\n\t\t// Sorting here creates groups of similar UUID paths\n\t\tsorted_layers.sort();\n\t\tsorted_layers.dedup_by(|a, b| a.starts_with(b));\n\t\tsorted_layers.into_iter().map(|mut path| {\n\t\t\tlet layer = path.pop().expect(\"Path should not be empty\");\n\t\t\tassert!(\n\t\t\t\tlayer != LayerNodeIdentifier::ROOT_PARENT,\n\t\t\t\t\"The root parent cannot be selected, so it cannot be a shallowest selected layer\"\n\t\t\t);\n\t\t\tlayer\n\t\t})\n\t}\n\n\tpub fn shallowest_unique_layers_sorted(&self, network_path: &[NodeId]) -> Vec<LayerNodeIdentifier> {\n\t\tlet all_layers_to_group = self.shallowest_unique_layers(network_path).collect::<Vec<_>>();\n\t\t// Ensure nodes are grouped in the correct order\n\t\tlet mut all_layers_to_group_sorted = Vec::new();\n\t\tfor descendant in LayerNodeIdentifier::ROOT_PARENT.descendants(self.document_metadata()) {\n\t\t\tif all_layers_to_group.contains(&descendant) {\n\t\t\t\tall_layers_to_group_sorted.push(descendant);\n\t\t\t};\n\t\t}\n\t\tall_layers_to_group_sorted","sourceCodeStart":1021,"sourceCodeEnd":1057,"githubUrl":"https://github.com/GraphiteEditor/Graphite/blob/c507b356453361e31638b8bff8f6d46b6da2961e/editor/src/messages/portfolio/document/utility_types/network_interface/queries.rs#L1021-L1057","documentation":"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.","triggerScenarios":"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().","commonSituations":"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.","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"],"exampleFix":"// before\nlet layer = path.pop().expect(\"Path should not be empty\");\n\n// after\nlet Some(layer) = path.pop() else {\n\tlog::error!(\"Empty ancestor path in shallowest_unique_layers\");\n\treturn LayerNodeIdentifier::ROOT_PARENT;\n};","handlingStrategy":"validation","validationCode":"// skip selections whose metadata has been removed before popping\nlet selected: Vec<_> = selection.iter().filter(|layer| metadata.get_relations(layer).is_some()).collect();","typeGuard":"fn has_relations(layer: LayerNodeIdentifier, metadata: &DocumentMetadata) -> bool {\n\tmetadata.get_relations(layer).is_some()\n}","tryCatchPattern":null,"preventionTips":["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"],"tags":["rust","selection","layer-tree","iterator","panic"],"backgroundTag":"unexpected-empty-collection","analyzedSha":"c507b356453361e31638b8bff8f6d46b6da2961e","analyzedAt":"2026-08-16T21:57:18.596Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}