facebook/lexical · error
Table row target index out of range
Error message
Table row target index out of range
What it means
$insertTableRow checks that targetIndex points at an existing row of the table before inserting. If targetIndex is negative or >= the current row count, the anchor row cannot be found and the function throws rather than silently appending.
Source
Thrown at packages/lexical-table/src/LexicalTableUtils.ts:195
const targetRowNode = tableRows[indexToDelete];
targetRowNode.remove();
return tableNode;
}
/**
* @deprecated This function does not support merged cells. Use {@link $insertTableRowAtSelection} or {@link $insertTableRowAtNode} instead.
*/
export function $insertTableRow(
tableNode: TableNode,
targetIndex: number,
shouldInsertAfter = true,
rowCount: number,
table: TableDOMTable,
): TableNode {
const tableRows = tableNode.getChildren();
if (targetIndex >= tableRows.length || targetIndex < 0) {
throw new Error('Table row target index out of range');
}
const targetRowNode = tableRows[targetIndex];
if ($isTableRowNode(targetRowNode)) {
for (let r = 0; r < rowCount; r++) {
const tableRowCells = targetRowNode.getChildren();
const tableColumnCount = tableRowCells.length;
const newTableRowNode = $createTableRowNode();
for (let c = 0; c < tableColumnCount; c++) {
const tableCellFromTargetRow = tableRowCells[c];
invariant(
$isTableCellNode(tableCellFromTargetRow),
'Expected table cell',
);
View on GitHub (pinned to 76a22dcba9)
Solutions
- Clamp or validate targetIndex: keep it in [0, tableNode.getChildrenSize() - 1], flipping shouldInsertAfter when inserting past the end.
- Derive targetIndex inside the update from the live TableNode children, not from cached DOM row indices.
- For appending rows after the last row, pass targetIndex = lastRowIndex with shouldInsertAfter = true, not rowCount.
Example fix
// before $insertTableRow(tableNode, tableNode.getChildrenSize(), true, 1, dom); // after const last = tableNode.getChildrenSize() - 1; $insertTableRow(tableNode, last, true, 1, dom);
Defensive patterns
Strategy: validation
Validate before calling
const rowCount = tableNode.getChildrenSize(); const idx = Math.min(Math.max(targetIndex, 0), rowCount - 1); const after = targetIndex >= rowCount ? true : shouldInsertAfter; $insertTableRow(tableNode, idx, after, n, dom);
Type guard
function isRowTargetValid(tableNode: TableNode, idx: number): boolean {
return idx >= 0 && idx < tableNode.getChildrenSize();
} Try / catch
try {
$insertTableRow(tableNode, idx, true, 1, dom);
} catch (e) {
if (e.message === 'Table row target index out of range') {
$insertTableRow(tableNode, tableNode.getChildrenSize() - 1, true, 1, dom);
} else throw e;
} Prevention
- Append below last row using lastIndex + shouldInsertAfter, not rowCount as index
- Derive indices from live Lexical children, not DOM row positions
- Clamp user-provided indices before calling insert APIs
When it happens
Trigger: Calling $insertTableRow(tableNode, targetIndex, shouldInsertAfter, rowCount, tableDOM) with an out-of-bounds targetIndex — e.g. inserting 'below' the last row using children.length as an after-index, or an index computed from stale DOM state.
Common situations: Toolbar 'insert row below' on the last row using rows.length as the index; index derived from a detached row's getIndexInRange after concurrent edits; passing 0-based vs 1-based indices inconsistently.
Related errors
- Table column target index out of range
- Expected TableNode.
- Expected table cell to be inside of table row.
- Expected table cell to be inside of table.
- Row before insertion index does not exist.
AI-assisted analysis of facebook/lexical@76a22dcba9 (2026-08-31).
Data as JSON: /api/errors/eecb599282671bc1.
Report an issue: GitHub.