GraphiteEditor/Graphite · info
Footprint widget should return multiple rows
Error message
Footprint widget should return multiple rows
What it means
widgets is a fixed-length [LayoutGroup; 3] array (location, scale, resolution rows). split_last() on an array of statically known non-zero length always returns Some, so this .expect can never fire at runtime — it is a compile-time-known invariant expressed as an assertion. Its real job is to peel the resolution row off as the returned widget and store the location/scale rows in extra_widgets.
Source
Thrown at editor/src/messages/portfolio/document/node_graph/node_properties.rs:638
.label("Resolution")
.mode_range()
.min(0.)
.range_min(Some(1.))
.range_max(Some(100.))
.unit("%")
.on_update(parameter_widgets_info.update_value(move |x: &NumberInput| {
let resolution = (bounds * x.value.unwrap_or(100.) / 100.).as_uvec2().max((1, 1).into()).min((4000, 4000).into());
let footprint = Footprint { resolution, ..footprint };
TaggedValue::Footprint(footprint)
}))
.on_commit(commit_value)
.widget_instance(),
);
}
let widgets = [LayoutGroup::row(location_widgets), LayoutGroup::row(scale_widgets), LayoutGroup::row(resolution_widgets)];
let (last, rest) = widgets.split_last().expect("Footprint widget should return multiple rows");
*extra_widgets = rest.to_vec();
last.clone()
}
pub fn transform_widget(parameter_widgets_info: ParameterWidgetsInfo, extra_widgets: &mut Vec<LayoutGroup>) -> LayoutGroup {
let ParameterWidgetsInfo { document_node, node_id, index, .. } = parameter_widgets_info;
let mut location_widgets = start_widgets(¶meter_widgets_info);
location_widgets.push(Separator::new(SeparatorStyle::Unrelated).widget_instance());
let mut rotation_widgets = vec![TextLabel::new("").widget_instance()];
add_blank_assist(&mut rotation_widgets);
rotation_widgets.push(Separator::new(SeparatorStyle::Unrelated).widget_instance());
let mut scale_widgets = vec![TextLabel::new("").widget_instance()];
add_blank_assist(&mut scale_widgets);
scale_widgets.push(Separator::new(SeparatorStyle::Unrelated).widget_instance());
View on GitHub (pinned to c507b35645)
Solutions
- Classify it as unreachable; no runtime fix is required.
- Replace with array destructuring (let [a, b, last] = widgets;) so emptiness becomes a compile-time error instead of a hidden runtime assumption.
- If the collection ever becomes dynamically sized, handle None explicitly rather than expecting.
Example fix
// before
let (last, rest) = widgets.split_last().expect("Footprint widget should return multiple rows");
*extra_widgets = rest.to_vec();
last.clone()
// after — length is statically known, no runtime panic possible
let [location, scale, resolution] = widgets;
*extra_widgets = vec![location, scale];
resolution Defensive patterns
Strategy: validation
Prevention
- Destructure fixed-length arrays instead of split_last().expect so the compiler proves length
- Reserve .expect for conditions the type system cannot prove, never statically-known lengths
- When triaging crash strings, first classify reachability — a fixed 3-element array cannot yield None
When it happens
Trigger: Not reachable at runtime with the current three-element array literal. It could only panic if widgets were refactored into an empty array or a dynamically-built Vec whose length is not guaranteed.
Common situations: Developers finding this string in crash reports and hunting a bug that cannot exist; later refactors converting the array to a runtime Vec, which would newly make None possible.
Related errors
- Solidify Stroke node should exist
- In `check_layer()`: there should be a `target`
- Artboard should have a primary input
- Merge node
- Node
AI-assisted analysis of GraphiteEditor/Graphite@c507b35645 (2026-08-16).
Data as JSON: /api/errors/24174e0252cc6964.
Report an issue: GitHub.