GraphiteEditor/Graphite · error

Cannot find connected anchor

Error message

Cannot find connected anchor

What it means

update_selection_status decides whether the colinearity toggle applies to the single selected point: it first checks that the point has a handle pair (get_handle_pair is Some) and then expects get_anchor to return the connected anchor. The expect encodes the invariant that a point which owns handles in the computed vector always has a resolvable anchor. It fails when the PointId refers to handles whose anchor is no longer in the freshly computed (modified) vector — typically because the vector changed between the selection being built and this lookup, or the selection holds stale ids after edits/undo removed the anchor.

Source

Thrown at editor/src/messages/tool/tool_messages/path_tool.rs:663

		let document_to_viewport = metadata.document_to_viewport;
		let previous_mouse = document_to_viewport.transform_point2(self.previous_mouse_position);
		if previous_mouse == self.drag_start_pos {
			let tolerance = DVec2::splat(SELECTION_TOLERANCE);
			[self.drag_start_pos - tolerance, self.drag_start_pos + tolerance]
		} else {
			[self.drag_start_pos, previous_mouse]
		}
	}

	fn update_selection_status(&mut self, shape_editor: &mut ShapeState, document: &DocumentMessageHandler) {
		let selection_status = get_selection_status(&document.network_interface, shape_editor);

		self.can_toggle_colinearity = match &selection_status {
			SelectionStatus::None => false,
			SelectionStatus::One(single_selected_point) => {
				let vector = document.network_interface.compute_modified_vector(single_selected_point.layer).unwrap();
				if single_selected_point.id.get_handle_pair(&vector).is_some() {
					let anchor = single_selected_point.id.get_anchor(&vector).expect("Cannot find connected anchor");
					vector.all_connected(anchor).count() <= 2
				} else {
					false
				}
			}
			SelectionStatus::Multiple(_) => true,
		};
		self.selection_status = selection_status;
	}

	fn remove_saved_points(&mut self) {
		self.saved_points_before_anchor_select_toggle.clear();
	}

	fn reset_drill_through_cycle(&mut self) {
		self.last_drill_through_click_position = None;
		self.drill_through_cycle_index = 0;
	}

View on GitHub (pinned to c507b35645)

Solutions

  1. Replace the expect with if let Some(anchor) = ... and treat None as 'colinearity toggle unavailable' (set can_toggle_colinearity = false).
  2. Before acting on the selection, validate stored point ids against the current vector (e.g., filter selected ids through the computed vector's points) and drop stale ones.
  3. Recompute the vector immediately before both lookups so get_handle_pair and get_anchor observe the same data.
  4. If the panic reproduces from a saved document, minimize the file and check whether the shape's handles reference an anchor index past the anchor count.

Example fix

// before
if single_selected_point.id.get_handle_pair(&vector).is_some() {
	let anchor = single_selected_point.id.get_anchor(&vector).expect("Cannot find connected anchor");
	vector.all_connected(anchor).count() <= 2
}

// after
single_selected_point
	.id
	.get_handle_pair(&vector)
	.and_then(|_| single_selected_point.id.get_anchor(&vector))
	.map(|anchor| vector.all_connected(anchor).count() <= 2)
	.unwrap_or(false)
Defensive patterns

Strategy: validation

Validate before calling

// Validate the selected point against the freshly computed vector before use
let vector = document.network_interface.compute_modified_vector(single_selected_point.layer).unwrap();
let can_toggle = single_selected_point
	.id
	.get_handle_pair(&vector)
	.and_then(|_| single_selected_point.id.get_anchor(&vector))
	.map(|anchor| vector.all_connected(anchor).count() <= 2)
	.unwrap_or(false);

Type guard

fn point_has_valid_anchor(vector: &VectorData, point: &PointId) -> bool {
	point.get_handle_pair(vector).is_some() && point.get_anchor(vector).is_some()
}

Prevention

When it happens

Trigger: In the path tool, selecting exactly one handle/point of a bezier path (SelectionStatus::One) so that compute_modified_vector runs and get_handle_pair(&vector).is_some() passes, while single_selected_point.id.get_anchor(&vector) returns None — e.g., after the anchor was deleted in the same transaction, or the shape state was carried over from a previous vector version.

Common situations: Undo/redo sequences that leave ShapeState with ids from an older vector; plugins or scripted edits that mutate vector points without notifying the shape editor; racing edits where the document graph re-executes between selection and status update; corrupted path documents where handles exist without anchors.

Related errors


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