BookStackApp/BookStack · error · Error
Row before insertion index does not exist.
Error message
Row before insertion index does not exist.
What it means
$insertTableRow inserts rowCount new rows relative to targetIndex in the TableNode. After the basic range check, it requires the child at targetIndex to actually be a TableRowNode; if it is a TableRowNode it proceeds, otherwise this error is thrown. It fires when the table's children are not all row nodes, i.e. the target index points at unexpected or malformed child content.
Source
Thrown at resources/js/wysiwyg/lexical/table/LexicalTableUtils.ts:230
) {
headerState |= TableCellHeaderStates.COLUMN;
}
const tableCellNode = $createTableCellNode(headerState, 1, width);
tableCellNode.append($createParagraphNode());
newTableRowNode.append(tableCellNode);
}
if (shouldInsertAfter) {
targetRowNode.insertAfter(newTableRowNode);
} else {
targetRowNode.insertBefore(newTableRowNode);
}
}
} else {
throw new Error('Row before insertion index does not exist.');
}
return tableNode;
}
const getHeaderState = (
currentState: TableCellHeaderState,
possibleState: TableCellHeaderState,
): TableCellHeaderState => {
if (
currentState === TableCellHeaderStates.BOTH ||
currentState === possibleState
) {
return possibleState;
}
return TableCellHeaderStates.NO_STATUS;
};
View on GitHub (pinned to 18f8469a1c)
Solutions
- Validate table children before inserting: every child of the TableNode should pass $isTableRowNode; remove or normalize any non-row child.
- Prefer $insertTableRow__EXPERIMENTAL() which derives the target row from the current selection instead of a hand-computed index.
- Recompute targetIndex from the live TableNode (tableNode.getChildren()) immediately before the call rather than caching it.
- Wrap the call in try/catch inside your update and surface a user-facing message instead of crashing the editor transaction.
Example fix
// before const rows = tableNode.getChildren(); $insertTableRow(tableNode, rows.length - 1, true, 1, table); // after const rows = tableNode.getChildren(); const targetIndex = rows.findIndex($isTableRowNode); if (targetIndex === -1) return; $insertTableRow(tableNode, targetIndex, true, 1, table);
Defensive patterns
Strategy: validation
Validate before calling
function canInsertRow(tableNode) {
const rows = tableNode.getChildren();
return rows.length > 0 && rows.every($isTableRowNode);
} Type guard
function isInsertableTable(node) {
return $isTableNode(node) && node.getChildren().every((c) => $isTableRowNode(c));
} Try / catch
try {
editor.update(() => $insertTableRow(tableNode, targetIndex, true, 1, table));
} catch (e) {
if (e.message === 'Row before insertion index does not exist.') {
// log and skip; table structure is not row-pure
} else throw e;
} Prevention
- Always call table helpers inside editor.update() and re-read children immediately before use.
- Prefer the __EXPERIMENTAL selection-based variants over manual index arithmetic.
- Assert every TableNode child is a TableRowNode after custom transforms and parser passes.
When it happens
Trigger: Calling $insertTableRow(tableNode, targetIndex, ...) where tableRows[targetIndex] fails $isTableRowNode — e.g. targetIndex is valid numerically but the child at that position is not a TableRowNode (custom/non-row child appended to the table, or table structure corrupted by other transforms).
Common situations: Custom editor plugins appending non-row children directly to a TableNode; DOM/parser output producing tables whose children don't match Lexical's expected schema; using the low-level $insertTableRow instead of the selection-based $insertTableRow__EXPERIMENTAL with a stale or mutated table.
Related errors
- Table column target index out of range
- Cell not found in table.
- Cell not found at cords.
- Node at cords not TableCellNode.
- Table Element Not Found
AI-assisted analysis of BookStackApp/BookStack@18f8469a1c (2026-09-02).
Data as JSON: /api/errors/97b2544b6b9b9f6c.
Report an issue: GitHub.