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
- Replace the expect with if let Some(anchor) = ... and treat None as 'colinearity toggle unavailable' (set can_toggle_colinearity = false).
- 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.
- Recompute the vector immediately before both lookups so get_handle_pair and get_anchor observe the same data.
- 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
- Treat any 'lookup succeeded then second lookup expected' pattern as a bug risk: chain both lookups and default on None.
- Recompute the modified vector immediately before reading handles/anchors so both checks see the same data.
- Filter shape-editor selection ids through the current vector after undo/redo or structural edits.
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
- No state for selected layer
- ROOT_PARENT should have at least one layer when clicking
- ROOT_PARENT should have a layer child when clicking
- Star node can't be found
- Brush node does not exist
AI-assisted analysis of GraphiteEditor/Graphite@c507b35645 (2026-08-16).
Data as JSON: /api/errors/37385f0498d7d230.
Report an issue: GitHub.