facebook/lexical · error
Expected TableNode.
Error message
Expected TableNode.
What it means
TableObserver.$clearText() looks up the table node by the observer's stored tableNodeKey with $getNodeByKey. If the key no longer resolves to a live TableNode (undefined or a different node kind), this guard throws. It means the observer is operating against a table that has been removed from the current editor state (e.g. after an undo, transform, or selection change left stale state).
Source
Thrown at packages/lexical-table/src/LexicalTableObserver.ts:590
: null;
cellNodes.forEach((cellNode: TableCellNode) => {
anchor.set(cellNode.getKey(), 0, 'element');
focus.set(cellNode.getKey(), cellNode.getChildrenSize(), 'element');
formatSelection.formatText(type, alignFormatWith);
});
$setSelection(selection);
this.editor.dispatchCommand(SELECTION_CHANGE_COMMAND);
}
$clearText() {
const {editor} = this;
const tableNode = $getNodeByKey(this.tableNodeKey);
if (!$isTableNode(tableNode)) {
throw new Error('Expected TableNode.');
}
const selection = $getSelection();
invariant($isTableSelection(selection), 'Expected TableSelection');
const selectedNodes = selection.getNodes().filter($isTableCellNode);
// Check if the entire table is selected by verifying first and last cells
const firstRow = tableNode.getFirstChild();
const lastRow = tableNode.getLastChild();
const isEntireTableSelected =
selectedNodes.length > 0 &&
firstRow !== null &&
lastRow !== null &&
$isTableRowNode(firstRow) &&
$isTableRowNode(lastRow) &&View on GitHub (pinned to 76a22dcba9)
Solutions
- Check the table still exists before dispatching the clear: editor.read(() => $getNodeByKey(key) !== undefined) or re-derive the TableNode from the current selection.
- Do not hold TableObserver/tableNodeKey references across update boundaries; obtain the table fresh from the current selection.
- If the table was legitimately deleted, skip the clear instead of treating it as an error.
- In collaboration setups, wrap remote-sync-driven operations so removals received from Yjs invalidate pending local table operations.
Example fix
// before
observer.$clearText();
// after
editor.update(() => {
const tableNode = $getNodeByKey(observer.tableNodeKey);
if ($isTableNode(tableNode)) {
observer.$clearText();
}
}); Defensive patterns
Strategy: validation
Validate before calling
const stillValid = editor.read(() => $isTableNode($getNodeByKey(key)));
Type guard
function isLiveTable(key: NodeKey, editor: LexicalEditor): boolean {
return editor.read(() => $isTableNode($getNodeByKey(key)));
} Try / catch
try {
editor.update(() => $clearTable());
} catch (e) {
if (e instanceof Error && e.message === 'Expected TableNode.') return; // table already gone
throw e;
} Prevention
- Never cache TableObserver or node-key references across update boundaries
- Re-derive the table from the current selection before mutations
- Disable table commands when the selection no longer intersects a table
When it happens
Trigger: Calling $clearTable at a time when the TableNode keyed by this.tableNodeKey has been removed or replaced: the node was deleted in the pending update, the key became stale after undo/revert, or a node transform moved/replaced the table while a clear operation was in flight.
Common situations: Dispatching CLEAR_TABLE_COMMAND after deleting the table in the same update; collaborating editors where the table was removed remotely; custom transforms that recreate TableNodes (new keys) while the observer still holds the old key.
Related errors
- Expected table cell to be inside of table row.
- Expected table cell to be inside of table.
- Table row target index out of range
- Row before insertion index does not exist.
- Table column target index out of range
AI-assisted analysis of facebook/lexical@76a22dcba9 (2026-08-31).
Data as JSON: /api/errors/30ba562c0014fe42.
Report an issue: GitHub.