Textualize/textual · error · RowDoesNotExist
Row key {row_key!r} is not valid.
Error message
Row key {row_key!r} is not valid. What it means
Raised by DataTable.get_row when the supplied row_key is not present in the table's internal _row_locations mapping. Textual uses stable RowKey objects (or their string ids) to track rows independently of their visual position, so this error means no row with that key has ever been added (or it was removed/cleared). It is the canonical 'unknown row key' error for key-based row access.
Source
Thrown at src/textual/widgets/_data_table.py:1004
)
row_index = self._row_locations.get(row_key)
column_index = self._column_locations.get(column_key)
return Coordinate(row_index, column_index)
def get_row(self, row_key: RowKey | str) -> list[CellType]:
"""Get the values from the row identified by the given row key.
Args:
row_key: The key of the row.
Returns:
A list of the values contained within the row.
Raises:
RowDoesNotExist: When there is no row corresponding to the key.
"""
if row_key not in self._row_locations:
raise RowDoesNotExist(f"Row key {row_key!r} is not valid.")
cell_mapping: dict[ColumnKey, CellType] = self._data.get(row_key, {})
ordered_row: list[CellType] = [
cell_mapping[column.key] for column in self.ordered_columns
]
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:View on GitHub (pinned to 06dbeef4bb)
Solutions
- Verify the key exists first: if row_key in table.row_keys or catch RowDoesNotExist.
- Re-fetch keys from the table after clear()/re-population instead of caching them across rebuilds.
- Ensure you are passing the RowKey returned by add_row(), not an index or a ColumnKey.
- If rows were removed, guard with table.get_row_index(row_key) inside try/except RowDoesNotExist before calling get_row.
Example fix
// before
row = table.get_row(stale_key)
// after
from textual.widgets._data_table import RowDoesNotExist
try:
row = table.get_row(stale_key)
except RowDoesNotExist:
row = None Defensive patterns
Strategy: try-catch
Validate before calling
key_exists = row_key in table.row_keys row = table.get_row(row_key) if key_exists else None
Type guard
from textual.widgets._data_table import RowKey
def is_live_row_key(table: DataTable, key: object) -> bool:
return isinstance(key, (RowKey, str)) and key in table._row_locations Try / catch
from textual.widgets._data_table import RowDoesNotExist
try:
row = table.get_row(row_key)
except RowDoesNotExist:
row = None # treat as missing Prevention
- Never cache row keys across clear() calls
- Use keys returned by add_row()
- Guard lookups with row_keys membership when rows can be removed
When it happens
Trigger: Calling table.get_row(key) with a key that was never returned by add_row/add_columns+add_row, a key from a table that had clear() called on it, a key from a different DataTable instance, or a key of a row removed via remove_row.
Common situations: Reusing stale row keys captured before a table refresh/clear; passing a ColumnKey where a RowKey is expected; rebuilding table data asynchronously while a callback still holds old keys; typos in string keys supplied to add_row(key=...).
Related errors
- No row exists for row_key={row_key!r}
- Column key {column_key!r} is not valid.
- No column exists for column_key={column_key!r}
- Row index {row_index!r} is not valid.
- Column index {column_index!r} is not valid.
AI-assisted analysis of Textualize/textual@06dbeef4bb (2026-08-27).
Data as JSON: /api/errors/5b51b65c3b07047e.
Report an issue: GitHub.