slint-ui/slint · error · IndexError

row index out of range

Error message

row index out of range

What it means

SlintPyListModel.remove_row performs explicit bounds validation against the Python list backing the model and raises IndexError when row is negative or >= len(self.list). It means the caller asked to delete a row that does not exist — commonly a stale index after a previous removal, or code that used the row count instead of count-1. The list is left unchanged when this fires.

Solutions

  1. Check 0 <= row < model.row_count() before calling remove_row.
  2. Recompute indices after each deletion instead of reusing stale positions.
  3. Wrap the call in try/except IndexError to tolerate already-removed rows.

Example fix

# before
model.remove_row(idx)  # IndexError if idx stale

# after
if 0 <= idx < model.row_count():
    model.remove_row(idx)
Defensive patterns

Strategy: validation

Validate before calling

def safe_remove(model, row):
    if not 0 <= row < model.row_count():
        return False
    model.remove_row(row)
    return True

Try / catch

try:
    model.remove_row(idx)
except IndexError:
    pass  # already removed

Prevention

When it happens

Trigger: Calling list_model.remove_row(negative) or remove_row(index >= row_count); removing from a stale index after the model shrank; concurrent modifications invalidating a cached index.

Common situations: UI code caching row indices while other callbacks delete rows; off-by-one loops (`for i in range(len+1)`); deleting the same row twice.

Related errors


AI-assisted analysis of slint-ui/slint@3a7e700487 (2026-09-16). Data as JSON: /api/errors/286c64e147034baa. Report an issue: GitHub.

Appendix: source

Thrown at api/python/slint/slint/models.py:131

        self.list: list[T]
        if iterable is not None:
            self.list = list(iterable)
        else:
            self.list = []

    def row_count(self) -> int:
        return len(self.list)

    def row_data(self, row: int) -> T | None:
        return self.list[row]

    def set_row_data(self, row: int, value: T) -> None:
        self.list[row] = value
        super().notify_row_changed(row if row >= 0 else row + len(self.list))

    def remove_row(self, row: int) -> None:
        if row < 0 or row >= len(self.list):
            raise IndexError("row index out of range")
        del self.list[row]
        super().notify_row_removed(row, 1)

    def insert_row(self, row: int, value: T) -> None:
        if row < 0 or row > len(self.list):
            raise IndexError("row index out of range")
        self.insert(row, value)

    def __delitem__(self, key: int | slice) -> None:
        if isinstance(key, slice):
            rows = range(*key.indices(len(self.list)))
            if not rows:
                return
            if abs(rows.step) == 1:
                first = rows.start if rows.step > 0 else rows[-1]
                del self.list[key]
                super().notify_row_removed(first, len(rows))
            else:

View on GitHub (pinned to 3a7e700487)