GraphiteEditor/Graphite · error

Parent accessed via child should have children

Error message

Parent accessed via child should have children

What it means

can_be_clipped checks that a layer is NOT the last child of its stack (used to decide clipping). It does parent(metadata).is_some_and(|layer| layer.last_child(metadata).expect(...)). The expect encodes a DocumentMetadata invariant: if a node has a parent link, that parent must have child links (first_child/last_child). The panic fires when that invariant breaks - the parent's relations exist and point to a parent, but that parent's last_child is None, i.e. corrupted/half-updated layer tree metadata.

Source

Thrown at editor/src/messages/portfolio/document/utility_types/document_metadata.rs:362

	/// Does the layer have children? If so, then it is a folder.
	pub fn has_children(self, metadata: &DocumentMetadata) -> bool {
		self.first_child(metadata).is_some()
	}

	/// Is the layer a child of the given layer?
	pub fn is_child_of(self, metadata: &DocumentMetadata, parent: &LayerNodeIdentifier) -> bool {
		parent.children(metadata).any(|child| child == self)
	}

	/// Is the layer an ancestor of the given layer?
	pub fn is_ancestor_of(self, metadata: &DocumentMetadata, child: &LayerNodeIdentifier) -> bool {
		child.ancestors(metadata).any(|ancestor| ancestor == self)
	}

	/// Is the layer the last child of its stack? Used for clipping
	pub fn can_be_clipped(self, metadata: &DocumentMetadata) -> bool {
		self.parent(metadata)
			.is_some_and(|layer| layer.last_child(metadata).expect("Parent accessed via child should have children") != self)
	}

	/// Iterator over all direct children (excluding self and recursive children)
	pub fn children(self, metadata: &DocumentMetadata) -> AxisIter<'_> {
		AxisIter {
			layer_node: self.first_child(metadata),
			next_node: Self::next_sibling,
			metadata,
		}
	}

	pub fn downstream_siblings(self, metadata: &DocumentMetadata) -> AxisIter<'_> {
		AxisIter {
			layer_node: Some(self),
			next_node: Self::previous_sibling,
			metadata,
		}
	}

View on GitHub (pinned to c507b35645)

Solutions

  1. Make the check total: layer.last_child(metadata) != Some(self) so a missing child link simply reports 'cannot clip' instead of panicking
  2. Find the mutation path that broke the invariant (log parent/children state when it triggers) and fix its metadata bookkeeping
  3. Add a debug-mode metadata consistency validator that walks parent<->child links after every structural message

Example fix

// before
self.parent(metadata).is_some_and(|layer| layer.last_child(metadata).expect("Parent accessed via child should have children") != self)

// after
self.parent(metadata).is_some_and(|layer| layer.last_child(metadata) != Some(self))
Defensive patterns

Strategy: validation

Validate before calling

// total version of the check: missing child links simply mean 'cannot clip'
let clip_allowed = match self.parent(metadata) {
	Some(parent) => parent.last_child(metadata) != Some(self),
	None => false,
};

Type guard

fn parent_has_children(layer: LayerNodeIdentifier, metadata: &DocumentMetadata) -> bool {
	layer.first_child(metadata).is_some()
}

Prevention

When it happens

Trigger: Any structural graph edit (delete, move, reorder, undo/redo of layer operations) that writes parent pointers without maintaining the corresponding first_child/last_child links, or deserialized older documents whose metadata was not fully rebuilt.

Common situations: Undo of a layer deletion leaving a dangling parent relation; custom code calling NodeGraphMessage handlers out of order; bugs in metadata recomputation after loading migrated documents.

Related errors


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