BookStackApp/BookStack · error · Error

Expected TableNode.

Error message

Expected TableNode.

What it means

TableObserver.clearHighlight clears the current table cell highlight inside an editor.update. It fetches the node by this.tableNodeKey and throws 'Expected TableNode.' if the node is missing or is no longer a TableNode ($isTableNode fails) — typically because the table was deleted or the key is stale.

Source

Thrown at resources/js/wysiwyg/lexical/table/LexicalTableObserver.ts:181

    this.isHighlightingCells = false;
    this.anchorX = -1;
    this.anchorY = -1;
    this.focusX = -1;
    this.focusY = -1;
    this.tableSelection = null;
    this.anchorCellNodeKey = null;
    this.focusCellNodeKey = null;
    this.anchorCell = null;
    this.focusCell = null;
    this.hasHijackedSelectionStyles = false;

    this.enableHighlightStyle();

    editor.update(() => {
      const tableNode = $getNodeByKey(this.tableNodeKey);

      if (!$isTableNode(tableNode)) {
        throw new Error('Expected TableNode.');
      }

      const tableElement = editor.getElementByKey(this.tableNodeKey);

      if (!tableElement) {
        throw new Error('Expected to find TableElement in DOM');
      }

      const grid = getTable(tableElement);
      $updateDOMForSelection(editor, grid, null);
      $setSelection(null);
      editor.dispatchCommand(SELECTION_CHANGE_COMMAND, undefined);
    });
  }

  enableHighlightStyle() {
    const editor = this.editor;
    editor.update(() => {

View on GitHub (pinned to 18f8469a1c)

Solutions

  1. Check $getNodeByKey(this.tableNodeKey) with $isTableNode before calling clearHighlight, and skip if not a table.
  2. Tear down/dispose the TableObserver when the table node is removed so clearHighlight is never called on a dead key.
  3. Wrap clearHighlight in try/catch and treat the error as 'table already gone' (clear state, return).
  4. Revalidate the observer's key after undo/redo or history-driven state replacements.

Example fix

// before
observer.clearHighlight(); // throws after table deletion

// after
const node = $getNodeByKey(observer.tableNodeKey);
if ($isTableNode(node)) {
  observer.clearHighlight();
}
Defensive patterns

Strategy: type-guard

Validate before calling

const node = $getNodeByKey(observer.tableNodeKey);
if (!$isTableNode(node)) return; // table deleted — skip highlight clearing

Type guard

function $isLiveTableNode(key: string): boolean {
  return $isTableNode($getNodeByKey(key));
}

Try / catch

try {
  observer.clearHighlight();
} catch (e) {
  if (e instanceof Error && (e.message === 'Expected TableNode.' || e.message === 'Expected to find TableElement in DOM')) {
    return; // table already removed
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling updateTableTableSelection (which calls clearHighlight) after the table node was removed from the editor state, or when the observer's tableNodeKey no longer resolves to a TableNode (node replaced by a different node type at the same key scenario, or null from $getNodeByKey).

Common situations: Deleting a table while it had an active cell selection/highlight then dispatching a selection update; stale observers kept alive after node removal; undo/redo replacing nodes and invalidating cached keys.

Related errors


AI-assisted analysis of BookStackApp/BookStack@18f8469a1c (2026-09-02). Data as JSON: /api/errors/eb2540afc1859394. Report an issue: GitHub.