{"record":{"id":"f58c0865e5e14e79","repo":"slint-ui/slint","slug":"index-was-tested-to-be-valid","errorCode":null,"errorMessage":"Index was tested to be valid","messagePattern":"Index was tested to be valid","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"tools/lsp/preview/ui/brushes.rs","lineNumber":259,"sourceCode":"    model: slint::ModelRc<ui::GradientStop>,\n    row: i32,\n) -> ui::GradientStop {\n    let row_usize = row as usize;\n    if row < 0 || row_usize > model.row_count() {\n        return fallback_gradient_stop(0.0);\n    }\n\n    let (prev, next) = if row_usize == 0 {\n        let first_stop = model.row_data(0).unwrap_or(fallback_gradient_stop(0.0));\n        let very_first_stop = ui::GradientStop { position: 0.0, color: first_stop.color };\n        (very_first_stop.clone(), very_first_stop)\n    } else if row_usize == model.row_count() {\n        let last_stop = model.row_data(row_usize - 1).unwrap_or(fallback_gradient_stop(1.0));\n        let very_last_stop = ui::GradientStop { position: 1.0, color: last_stop.color };\n        (very_last_stop.clone(), very_last_stop)\n    } else {\n        (\n            model.row_data(row_usize - 1).expect(\"Index was tested to be valid\"),\n            model.row_data(row_usize).expect(\"index was tested to be valid\"),\n        )\n    };\n\n    interpolate(prev, next, 0.5)\n}\n\nfn suggest_gradient_stop_at_position(\n    model: slint::ModelRc<ui::GradientStop>,\n    position: f32,\n) -> ui::GradientStop {\n    let position = position.clamp(0.0, 1.0);\n\n    if model.row_count() == 0 {\n        return fallback_gradient_stop(position);\n    }\n\n    let mut prev = model.row_data(0).expect(\"Not empty\");","sourceCodeStart":241,"sourceCodeEnd":277,"githubUrl":"https://github.com/slint-ui/slint/blob/bb937076de3f7919766c1f25e2e969367cf77e9a/tools/lsp/preview/ui/brushes.rs#L241-L277","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\n(\n    model.row_data(row_usize - 1).expect(\"Index was tested to be valid\"),\n    model.row_data(row_usize).expect(\"index was tested to be valid\"),\n)\n// after\n(\n    model.row_data(row_usize - 1).unwrap_or(fallback_gradient_stop(0.0)),\n    model.row_data(row_usize).unwrap_or(fallback_gradient_stop(1.0)),\n)","handlingStrategy":"validation","validationCode":"let n = model.row_count();\nlet ok = row >= 0 && (row as usize) < n\n    && model.row_data(row as usize - 1).is_some()\n    && model.row_data(row as usize).is_some();\nif !ok { return fallback_gradient_stop(0.0); }","typeGuard":"fn valid_row(model: &slint::ModelRc<ui::GradientStop>, row: usize) -> bool {\n    row < model.row_count() && model.row_data(row).is_some()\n}","tryCatchPattern":"// Rust: panics abort the thread; avoid by using unwrap_or with the existing fallbacks\nlet prev = model.row_data(row_usize - 1).unwrap_or(fallback_gradient_stop(0.0));\nlet next = model.row_data(row_usize).unwrap_or(fallback_gradient_stop(1.0));","preventionTips":["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."],"tags":["rust","panic","lsp","model","index-out-of-bounds","ui"],"backgroundTag":"index-out-of-bounds","analyzedSha":"bb937076de3f7919766c1f25e2e969367cf77e9a","analyzedAt":"2026-09-16T01:37:20.251Z","contentChangedAt":"2026-09-16T01:37:20.251Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}