slint-ui/slint · error
Index was tested to be valid
Error message
Index was tested to be valid
What it means
Internal invariant panic from `.expect("Index was tested to be valid")` on `model.row_data(...)` in `suggest_gradient_stop_at_row` (tools/lsp/preview/ui/brushes.rs:259). The function validates `0 <= row <= model.row_count()` at the top, so the intermediate-branch accesses to `row_usize - 1` and `row_usize` are expected to be in range. It computes the midpoint gradient stop suggestion when a row is inserted into the gradient editor. Panics if a live `ModelRc` shrank or misbehaved between the `row_count()` check and the `row_data` calls.
Solutions
- Fix the Model implementation so row_count() and row_data() stay consistent (row_data(i) is Some for all i < row_count()).
- Use the existing graceful pattern instead of expect: `model.row_data(row_usize - 1).unwrap_or(fallback_gradient_stop(0.0))`.
- Re-read row_count() immediately before each row_data call if the model may be mutated concurrently, or clone the data first.
- Ensure the model isn't swapped or cleared on the UI thread while suggestion callbacks run.
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
(
model.row_data(row_usize - 1).unwrap_or(fallback_gradient_stop(0.0)),
model.row_data(row_usize).unwrap_or(fallback_gradient_stop(1.0)),
) Defensive patterns
Strategy: validation
Validate before calling
let n = model.row_count();
let ok = row >= 0 && (row as usize) < n
&& model.row_data(row as usize - 1).is_some()
&& model.row_data(row as usize).is_some();
if !ok { return fallback_gradient_stop(0.0); } Type guard
fn valid_row(model: &slint::ModelRc<ui::GradientStop>, row: usize) -> bool {
row < model.row_count() && model.row_data(row).is_some()
} Try / catch
// Rust: panics abort the thread; avoid by using unwrap_or with the existing fallbacks let prev = model.row_data(row_usize - 1).unwrap_or(fallback_gradient_stop(0.0)); let next = model.row_data(row_usize).unwrap_or(fallback_gradient_stop(1.0));
Prevention
- Implement Model::row_count and row_data consistently; row_data(i) must be Some for every i < row_count().
- Do not mutate or reassign the backing VecModel while UI callbacks compute suggestions.
- Re-check row_count() right before indexed row_data calls on dynamic models.
- Favor unwrap_or(fallback) over expect for model accesses — models are dynamic by design.
When it happens
Trigger: A dynamic model (e.g. VecModel backing the gradient-stop list in the preview UI) whose row_count changes concurrently with this call, or a custom Model implementation whose row_count()/row_data() disagree (row_count reports N but row_data(i) returns None for i < N). row < 0 or row > row_count takes the safe fallback path, so only mid-row indices with an inconsistent model panic.
Common situations: Custom slint::Model implementations that report a stale row_count; preview UI updates where the gradient stop model is reset/reassigned while a row-insertion suggestion is computed; off-by-one bugs in a wrapper model.
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/f58c0865e5e14e79.
Report an issue: GitHub.
Appendix: source
Thrown at tools/lsp/preview/ui/brushes.rs:259
model: slint::ModelRc<ui::GradientStop>,
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");View on GitHub (pinned to bb937076de)