Textualize/textual · error · RowDoesNotExist
Row index {row_index!r} is not valid.
Error message
Row index {row_index!r} is not valid. What it means
Raised by DataTable.get_row_at when the integer row_index fails is_valid_row_index (negative beyond -row_count, or >= row_count). Index-based row access reflects the current visual/sort order, so out-of-range indices raise RowDoesNotExist. get_row_at then delegates to get_row, so the row must exist at that position.
Source
Thrown at src/textual/widgets/_data_table.py:1026
]
return ordered_row
def get_row_at(self, row_index: int) -> list[CellType]:
"""Get the values from the cells in a row at a given index. This will
return the values from a row based on the rows _current position_ in
the table.
Args:
row_index: The index of the row.
Returns:
A list of the values contained in the row.
Raises:
RowDoesNotExist: If there is no row with the given index.
"""
if not self.is_valid_row_index(row_index):
raise RowDoesNotExist(f"Row index {row_index!r} is not valid.")
row_key = self._row_locations.get_key(row_index)
return self.get_row(row_key)
def get_row_index(self, row_key: RowKey | str) -> int:
"""Return the current index for the row identified by row_key.
Args:
row_key: The row key to find the current index of.
Returns:
The current index of the specified row key.
Raises:
RowDoesNotExist: If the row key does not exist.
"""
if row_key not in self._row_locations:
raise RowDoesNotExist(f"No row exists for row_key={row_key!r}")
return self._row_locations.get(row_key)View on GitHub (pinned to 06dbeef4bb)
Solutions
- Check bounds first: 0 <= index < table.row_count (or -row_count <= index < row_count).
- Prefer key-based access (get_row with the key from add_row) when order can change.
- Re-read table.row_count immediately before access after any add/remove/sort/clear.
- Catch textual.widgets._data_table.RowDoesNotExist as a defensive fallback.
Example fix
// before
row = table.get_row_at(current_index)
// after
if table.is_valid_row_index(current_index):
row = table.get_row_at(current_index)
else:
row = None Defensive patterns
Strategy: validation
Validate before calling
row = table.get_row_at(i) if table.is_valid_row_index(i) else None
Try / catch
from textual.widgets._data_table import RowDoesNotExist
try:
row = table.get_row_at(i)
except RowDoesNotExist:
row = None Prevention
- Re-read table.row_count right before index access
- Prefer key-based access when sorting/filtering can reorder rows
- Bounds-check with is_valid_row_index
When it happens
Trigger: Calling get_row_at with an index >= table.row_count, a negative index smaller than -row_count, or using an index captured before sorting/filtering/row removal changed the ordering.
Common situations: Iterating with a stale row count after async row deletion; assuming a fixed row count after re-sorting; off-by-one errors when iterating range(table.row_count + 1); using indices while a cursor/selection callback races with updates.
Related errors
- Column index {column_index!r} is not valid.
- The row key {row_key!r} already exists.
- Row key {row_key!r} is not valid.
- No row exists for row_key={row_key!r}
- Column key {column_key!r} is not valid.
AI-assisted analysis of Textualize/textual@06dbeef4bb (2026-08-27).
Data as JSON: /api/errors/d22c3e3c11616bdb.
Report an issue: GitHub.