GraphiteEditor/Graphite · error
No handle position
Error message
No handle position
What it means
The same comparator as error 122 also unwraps the current handle's own position with point.get_position(&vector).expect("No handle position") (shape_editor.rs:1162). get_position (vector-types/src/vector/misc.rs:449) resolves PrimaryHandle/EndHandle via vector.segment_from_id(id).and_then(|bezier| bezier.handle_start()/handle_end()) — it is None when the SegmentId is gone from segment_domain or the bezier reports no distinct handle for that end. Because the comparator runs for every pair of candidates, one stale or handle-less segment among the selection is enough to panic.
Source
Thrown at editor/src/messages/tool/common_functionality/shape_editor.rs:1162
continue;
}
// Here we take handles as the current handle and the most opposite non-colinear-handle
let is_handle_colinear = |handle: HandleId| -> bool { vector.colinear_manipulators.iter().any(|&handles| handles[0] == handle || handles[1] == handle) };
let other_handles = if matches!(point, ManipulatorPointId::Anchor(_)) {
point.get_handle_pair(&vector)
} else {
point.get_all_connected_handles(&vector).and_then(|handles| {
let mut non_colinear_handles = handles.iter().filter(|&handle| !is_handle_colinear(*handle)).clone().collect::<Vec<_>>();
// Sort these by angle from the current handle
non_colinear_handles.sort_by(|&handle_a, &handle_b| {
let anchor = point.get_anchor_position(&vector).expect("No anchor position for handle");
let orig_handle_pos = point.get_position(&vector).expect("No handle position");
let a_pos = handle_a.to_manipulator_point().get_position(&vector).expect("No handle position");
let b_pos = handle_b.to_manipulator_point().get_position(&vector).expect("No handle position");
let v_orig = (orig_handle_pos - anchor).normalize_or_zero();
let v_a = (a_pos - anchor).normalize_or_zero();
let v_b = (b_pos - anchor).normalize_or_zero();
let angle_a = v_orig.angle_to(v_a).abs();
let angle_b = v_orig.angle_to(v_b).abs();
// Sort by descending angle (180° is furthest)
angle_b.partial_cmp(&angle_a).unwrap_or(std::cmp::Ordering::Equal)
});
let current = match point {
ManipulatorPointId::EndHandle(segment) => HandleId::end(segment),
ManipulatorPointId::PrimaryHandle(segment) => HandleId::primary(segment),
ManipulatorPointId::Anchor(_) => unreachable!(),View on GitHub (pinned to c507b35645)
Solutions
- Pre-filter non_colinear_handles (and bail out early if the current point's own position is None) before entering sort_by
- Use a let-else returning Ordering::Equal for missing positions so stale ids are skipped, not fatal
- Refresh or prune selection state from compute_modified_vector before the interaction that triggers the sort
Example fix
// before
let orig_handle_pos = point.get_position(&vector).expect("No handle position");
// after
let Some(orig_handle_pos) = point.get_position(&vector) else {
return std::cmp::Ordering::Equal; // no distinct handle position; keep current order
}; Defensive patterns
Strategy: validation
Validate before calling
// Early-exit when the dragged handle itself cannot be positioned:
let (Some(_anchor), Some(orig_handle_pos)) = (point.get_anchor_position(&vector), point.get_position(&vector)) else {
return None; // stale or handle-less segment; skip opposite-handle logic
}; Type guard
fn is_positionable(point: &ManipulatorPointId, vector: &Vector) -> bool {
point.get_position(vector).is_some()
} Prevention
- Filter handles by get_position().is_some() before entering the comparator
- Treat segment/handle ids as ephemeral across graph recomputations; re-derive selection after any vector mutation
- Write comparator fallbacks as Ordering::Equal, not unwrap/expect, so partial data degrades silently instead of crashing
When it happens
Trigger: Sorting non-colinear handles for the opposite-handle behavior when the dragged handle's own segment no longer exists in the recomputed vector, or when its bezier has no stored start/end handle (e.g. segment reduced to a line after colinear-handle operations).
Common situations: Dragging a handle right after an undo that removed its segment; converting segments between curve types while selected; fuzz/automation sequences issuing drags between graph mutations.
Related errors
- No anchor position for handle
- Handle cannot be converted
- If `selection_shape` is a polygon then subpath is constructe
- Cannot find state for layer
- Solidify Stroke node should exist
AI-assisted analysis of GraphiteEditor/Graphite@c507b35645 (2026-08-16).
Data as JSON: /api/errors/df9709159e62d2c7.
Report an issue: GitHub.