{"record":{"id":"547d9fb3ddd3b32c","repo":"slint-ui/slint","slug":"index-was-tested-to-be-valid-brushes","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":260,"sourceCode":"    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\");\n    prev.position = 0.0;","sourceCodeStart":242,"sourceCodeEnd":278,"githubUrl":"https://github.com/slint-ui/slint/blob/bb937076de3f7919766c1f25e2e969367cf77e9a/tools/lsp/preview/ui/brushes.rs#L242-L278","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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"],"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\nlet prev = model.row_data(row_usize - 1)\n    .unwrap_or_else(|| fallback_gradient_stop(0.0));\nlet next = model.row_data(row_usize)\n    .unwrap_or_else(|| fallback_gradient_stop(1.0));\n(prev, next)","handlingStrategy":"validation","validationCode":"if (row > 0 && row < model.row_count()) {\n    // safe to call suggest_gradient_stop_at_row\n}","typeGuard":"fn row_in_bounds(model: &ModelRc<ui::GradientStop>, row: usize) -> bool {\n    row > 0 && row < model.row_count()\n}","tryCatchPattern":null,"preventionTips":["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"],"tags":["rust","lsp","panic","model","index-out-of-bounds"],"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"}