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_sorted

View on GitHub (pinned to c507b35645)

Solutions

  1. Use filter_map(|mut path| path.pop()) so empty paths are skipped instead of panicking
  2. Clean the selection set whenever node deletion updates metadata, so ancestors() always resolves
  3. 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

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


AI-assisted analysis of GraphiteEditor/Graphite@c507b35645 (2026-08-16). Data as JSON: /api/errors/fdecd586d722e3f7. Report an issue: GitHub.