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

  1. Re-check model.row_count() immediately before calling and clamp the row index into 0..row_count
  2. Re-fetch the row index from the UI selection right before use instead of caching it
  3. Replace the two expect() calls with unwrap_or_else using fallback_gradient_stop to degrade gracefully
  4. 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

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


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)