{"record":{"id":"eb2540afc1859394","repo":"BookStackApp/BookStack","slug":"expected-tablenode","errorCode":null,"errorMessage":"Expected TableNode.","messagePattern":"Expected TableNode\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"resources/js/wysiwyg/lexical/table/LexicalTableObserver.ts","lineNumber":181,"sourceCode":"    this.isHighlightingCells = false;\n    this.anchorX = -1;\n    this.anchorY = -1;\n    this.focusX = -1;\n    this.focusY = -1;\n    this.tableSelection = null;\n    this.anchorCellNodeKey = null;\n    this.focusCellNodeKey = null;\n    this.anchorCell = null;\n    this.focusCell = null;\n    this.hasHijackedSelectionStyles = false;\n\n    this.enableHighlightStyle();\n\n    editor.update(() => {\n      const tableNode = $getNodeByKey(this.tableNodeKey);\n\n      if (!$isTableNode(tableNode)) {\n        throw new Error('Expected TableNode.');\n      }\n\n      const tableElement = editor.getElementByKey(this.tableNodeKey);\n\n      if (!tableElement) {\n        throw new Error('Expected to find TableElement in DOM');\n      }\n\n      const grid = getTable(tableElement);\n      $updateDOMForSelection(editor, grid, null);\n      $setSelection(null);\n      editor.dispatchCommand(SELECTION_CHANGE_COMMAND, undefined);\n    });\n  }\n\n  enableHighlightStyle() {\n    const editor = this.editor;\n    editor.update(() => {","sourceCodeStart":163,"sourceCodeEnd":199,"githubUrl":"https://github.com/BookStackApp/BookStack/blob/18f8469a1c72f8cc8497e9372635e6dea5028071/resources/js/wysiwyg/lexical/table/LexicalTableObserver.ts#L163-L199","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Check $getNodeByKey(this.tableNodeKey) with $isTableNode before calling clearHighlight, and skip if not a table.","Tear down/dispose the TableObserver when the table node is removed so clearHighlight is never called on a dead key.","Wrap clearHighlight in try/catch and treat the error as 'table already gone' (clear state, return).","Revalidate the observer's key after undo/redo or history-driven state replacements."],"exampleFix":"// before\nobserver.clearHighlight(); // throws after table deletion\n\n// after\nconst node = $getNodeByKey(observer.tableNodeKey);\nif ($isTableNode(node)) {\n  observer.clearHighlight();\n}","handlingStrategy":"type-guard","validationCode":"const node = $getNodeByKey(observer.tableNodeKey);\nif (!$isTableNode(node)) return; // table deleted — skip highlight clearing","typeGuard":"function $isLiveTableNode(key: string): boolean {\n  return $isTableNode($getNodeByKey(key));\n}","tryCatchPattern":"try {\n  observer.clearHighlight();\n} catch (e) {\n  if (e instanceof Error && (e.message === 'Expected TableNode.' || e.message === 'Expected to find TableElement in DOM')) {\n    return; // table already removed\n  }\n  throw e;\n}","preventionTips":["Dispose observers when their table node is removed","Validate the node key resolves to a TableNode before highlight updates","Revalidate keys after undo/redo operations"],"tags":["lexical","table","observer","stale-reference","node-validation"],"backgroundTag":"expected-table-node","analyzedSha":"18f8469a1c72f8cc8497e9372635e6dea5028071","analyzedAt":"2026-09-02T19:49:33.068Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}