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

  1. Fix the Model implementation so row_count() and row_data() stay consistent (row_data(i) is Some for all i < row_count()).
  2. Use the existing graceful pattern instead of expect: `model.row_data(row_usize - 1).unwrap_or(fallback_gradient_stop(0.0))`.
  3. Re-read row_count() immediately before each row_data call if the model may be mutated concurrently, or clone the data first.
  4. 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

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


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)