slint-ui/slint · error
index was tested to be valid
Error message
index was tested to be valid
What it means
This is a panic from `.expect("index was tested to be valid")` on a `ModelRc::row_data` call inside `suggest_gradient_stop_at_row` in the LSP preview. The function assumes the row index it receives was already bounds-checked before use, so when an out-of-range row still reaches it, the model returns `None` and the expect panics. It is an internal invariant violation, not a user-facing error.
Solutions
- Re-check model.row_count() immediately before calling and clamp the row index into 0..row_count
- Re-fetch the row index from the UI selection right before use instead of caching it
- Replace the two expect() calls with unwrap_or_else using fallback_gradient_stop to degrade gracefully
- Report the bug to the Slint LSP maintainers with reproduction steps
Example fix
// before
(
model.row_data(row_usize - 1).expect("Index was tested to be valid"),
model.row_data(row_usize).expect("index was tested to be valid"),
)
// after
let prev = model.row_data(row_usize - 1)
.unwrap_or_else(|| fallback_gradient_stop(0.0));
let next = model.row_data(row_usize)
.unwrap_or_else(|| fallback_gradient_stop(1.0));
(prev, next) Defensive patterns
Strategy: validation
Validate before calling
if (row > 0 && row < model.row_count()) {
// safe to call suggest_gradient_stop_at_row
} Type guard
fn row_in_bounds(model: &ModelRc<ui::GradientStop>, row: usize) -> bool {
row > 0 && row < model.row_count()
} Prevention
- Validate row indices against model.row_count() at the call site before every model access
- Avoid caching row indices across model mutations
- Prefer unwrap_or_else with a fallback over expect() in UI helper code
- Re-read selection state immediately before use
When it happens
Trigger: Calling suggest_gradient_stop_at_row with a row index >= model.row_count() or a negative/zero row that escapes the earlier branch (the code checks row_usize>0 but only the last-branch handles row_usize-1 for the final row; row 0 after the guard, or a stale row index after rows were removed from the model, triggers the panic).
Common situations: Clicking or editing a gradient-stop row in the LSP preview while the brushes model was just mutated (a stop deleted concurrently), or a stale row index cached from an earlier UI state being passed into the suggestion helper.
Related errors
- Index was tested to be valid
- EditorSession must have at least one preview
- Global was just valid
- just added if missing
- Length was checked
AI-assisted analysis of slint-ui/slint@bb937076de (2026-09-16).
Data as JSON: /api/errors/547d9fb3ddd3b32c.
Report an issue: GitHub.
Appendix: source
Thrown at tools/lsp/preview/ui/brushes.rs:260
row: i32,
) -> ui::GradientStop {
let row_usize = row as usize;
if row < 0 || row_usize > model.row_count() {
return fallback_gradient_stop(0.0);
}
let (prev, next) = if row_usize == 0 {
let first_stop = model.row_data(0).unwrap_or(fallback_gradient_stop(0.0));
let very_first_stop = ui::GradientStop { position: 0.0, color: first_stop.color };
(very_first_stop.clone(), very_first_stop)
} else if row_usize == model.row_count() {
let last_stop = model.row_data(row_usize - 1).unwrap_or(fallback_gradient_stop(1.0));
let very_last_stop = ui::GradientStop { position: 1.0, color: last_stop.color };
(very_last_stop.clone(), very_last_stop)
} else {
(
model.row_data(row_usize - 1).expect("Index was tested to be valid"),
model.row_data(row_usize).expect("index was tested to be valid"),
)
};
interpolate(prev, next, 0.5)
}
fn suggest_gradient_stop_at_position(
model: slint::ModelRc<ui::GradientStop>,
position: f32,
) -> ui::GradientStop {
let position = position.clamp(0.0, 1.0);
if model.row_count() == 0 {
return fallback_gradient_stop(position);
}
let mut prev = model.row_data(0).expect("Not empty");
prev.position = 0.0;View on GitHub (pinned to bb937076de)