{"record":{"id":"d0a27c4298ddd5cd","repo":"slint-ui/slint","slug":"row-index-index-is-out-of-bounds","errorCode":null,"errorMessage":"row index ${index} is out of bounds","messagePattern":"row index (.+?) is out of bounds","errorType":"exception","errorClass":"RangeError","httpStatus":null,"severity":"error","filePath":"api/node/typescript/models.ts","lineNumber":268,"sourceCode":"\n    /**\n     * Stores the given data on the given row index and notifies run-time about the changed row.\n     * @param row index in range 0..(rowCount() - 1).\n     * @param data new data item to store on the given row index\n     */\n    setRowData(row: number, data: T) {\n        this.#array[row] = data;\n        this.notifyRowDataChanged(row);\n    }\n\n    /**\n     * Remove a row from the array backing the model and notifies run-time about the removed row.\n     * Throws a RangeError when the index is out of bounds.\n     * @param index index of the row to remove.\n     */\n    removeRow(index: number): void {\n        if (index < 0 || index >= this.#array.length) {\n            throw new RangeError(`row index ${index} is out of bounds`);\n        }\n        this.remove(index, 1);\n    }\n\n    /**\n     * Insert a new row into the array backing the model at the specified index and notifies run-time about the added row.\n     * Throws a RangeError when the index is out of bounds.\n     * @param index index at which to insert the new row.\n     * @param data data item to store in the new row.\n     */\n    insertRow(index: number, data: T): void {\n        if (index < 0 || index > this.#array.length) {\n            throw new RangeError(`row index ${index} is out of bounds`);\n        }\n        this.splice(index, 0, data);\n    }\n\n    /**","sourceCodeStart":250,"sourceCodeEnd":286,"githubUrl":"https://github.com/slint-ui/slint/blob/bb937076de3f7919766c1f25e2e969367cf77e9a/api/node/typescript/models.ts#L250-L286","documentation":"RangeArrayModel.removeRow validates the requested index against the current array length before deleting; this RangeError is the guard firing. It means the caller passed an index outside 0..rowCount()-1 — typically a stale index captured before a prior removal shrank the model, an off-by-one from using rowCount() itself, or a negative value. The model's backing array is untouched when this throws.","triggerScenarios":"removeRow called with a negative index, an index equal to or larger than rowCount, or after the array was shrunk by another removal.","commonSituations":"Off-by-one in delete loops, stale indices kept after previous removals, race between UI row index and array contents.","solutions":["Check 0 <= index < model.rowCount() before calling removeRow.","Re-fetch rowCount after each removal instead of caching indices.","Clamp the index: Math.min(Math.max(index,0), rowCount()-1) when appropriate."],"exampleFix":"// before\nmodel.removeRow(selectedIndex);\n// after\nif (selectedIndex >= 0 && selectedIndex < model.rowCount()) model.removeRow(selectedIndex);","handlingStrategy":"validation","validationCode":"if (!Number.isInteger(index) || index < 0 || index >= model.rowCount()) {\n  throw new RangeError(`cannot remove row ${index}; rowCount=${model.rowCount()}`);\n}","typeGuard":"const canRemoveAt = (m: { rowCount(): number }, i: number): boolean =>\n  Number.isInteger(i) && i >= 0 && i < m.rowCount();","tryCatchPattern":"try {\n  model.removeRow(index);\n} catch (e) {\n  if (e instanceof RangeError) {\n    console.warn(`skipping removal: ${e.message}`);\n  } else throw e;\n}","preventionTips":["Always validate against rowCount() immediately before mutating.","Do not cache row indices across mutations.","Prefer row handles/ids resolved to a fresh index at call time."],"tags":["typescript","model","range-error"],"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"}