{"record":{"id":"d22c3e3c11616bdb","repo":"Textualize/textual","slug":"row-index-row-index-r-is-not-valid","errorCode":null,"errorMessage":"Row index {row_index!r} is not valid.","messagePattern":"Row index (.+?) is not valid\\.","errorType":"exception","errorClass":"RowDoesNotExist","httpStatus":null,"severity":"error","filePath":"src/textual/widgets/_data_table.py","lineNumber":1026,"sourceCode":"        ]\n        return ordered_row\n\n    def get_row_at(self, row_index: int) -> list[CellType]:\n        \"\"\"Get the values from the cells in a row at a given index. This will\n        return the values from a row based on the rows _current position_ in\n        the table.\n\n        Args:\n            row_index: The index of the row.\n\n        Returns:\n            A list of the values contained in the row.\n\n        Raises:\n            RowDoesNotExist: If there is no row with the given index.\n        \"\"\"\n        if not self.is_valid_row_index(row_index):\n            raise RowDoesNotExist(f\"Row index {row_index!r} is not valid.\")\n        row_key = self._row_locations.get_key(row_index)\n        return self.get_row(row_key)\n\n    def get_row_index(self, row_key: RowKey | str) -> int:\n        \"\"\"Return the current index for the row identified by row_key.\n\n        Args:\n            row_key: The row key to find the current index of.\n\n        Returns:\n            The current index of the specified row key.\n\n        Raises:\n            RowDoesNotExist: If the row key does not exist.\n        \"\"\"\n        if row_key not in self._row_locations:\n            raise RowDoesNotExist(f\"No row exists for row_key={row_key!r}\")\n        return self._row_locations.get(row_key)","sourceCodeStart":1008,"sourceCodeEnd":1044,"githubUrl":"https://github.com/Textualize/textual/blob/06dbeef4bb70fb718236aa418ed658ef4667a126/src/textual/widgets/_data_table.py#L1008-L1044","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nrow = table.get_row_at(current_index)\n\n// after\nif table.is_valid_row_index(current_index):\n    row = table.get_row_at(current_index)\nelse:\n    row = None","handlingStrategy":"validation","validationCode":"row = table.get_row_at(i) if table.is_valid_row_index(i) else None","typeGuard":null,"tryCatchPattern":"from textual.widgets._data_table import RowDoesNotExist\ntry:\n    row = table.get_row_at(i)\nexcept RowDoesNotExist:\n    row = None","preventionTips":["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"],"tags":["data-table","index-out-of-range","textual","row"],"backgroundTag":"index-out-of-range","analyzedSha":"06dbeef4bb70fb718236aa418ed658ef4667a126","analyzedAt":"2026-08-27T02:36:57.214Z","schemaVersion":2},"datasetVersion":"2026-08-27T03:17:27.898Z"}