{"record":{"id":"16feacbbd075ae0e","repo":"facebook/lexical","slug":"expected-table-cell-to-be-inside-of-table","errorCode":null,"errorMessage":"Expected table cell to be inside of table.","messagePattern":"Expected table cell to be inside of table\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/lexical-table/src/LexicalTableUtils.ts","lineNumber":128,"sourceCode":"  const node = $findMatchingParent(startingNode, n => $isTableRowNode(n));\n\n  if ($isTableRowNode(node)) {\n    return node;\n  }\n\n  throw new Error('Expected table cell to be inside of table row.');\n}\n\nexport function $getTableNodeFromLexicalNodeOrThrow(\n  startingNode: LexicalNode,\n): TableNode {\n  const node = $findMatchingParent(startingNode, n => $isTableNode(n));\n\n  if ($isTableNode(node)) {\n    return node;\n  }\n\n  throw new Error('Expected table cell to be inside of table.');\n}\n\nexport function $getTableRowIndexFromTableCellNode(\n  tableCellNode: TableCellNode,\n): number {\n  const tableRowNode = $getTableRowNodeFromTableCellNodeOrThrow(tableCellNode);\n  const tableNode = $getTableNodeFromLexicalNodeOrThrow(tableRowNode);\n  return tableNode.getChildren().findIndex(n => n.is(tableRowNode));\n}\n\nexport function $getTableColumnIndexFromTableCellNode(\n  tableCellNode: TableCellNode,\n): number {\n  const tableRowNode = $getTableRowNodeFromTableCellNodeOrThrow(tableCellNode);\n  return tableRowNode.getChildren().findIndex(n => n.is(tableCellNode));\n}\n\nexport type TableCellSiblings = {","sourceCodeStart":110,"sourceCodeEnd":146,"githubUrl":"https://github.com/facebook/lexical/blob/76a22dcba981b46119fcec45f935270707e26a57/packages/lexical-table/src/LexicalTableUtils.ts#L110-L146","documentation":"$getTableNodeFromLexicalNodeOrThrow climbs from the starting node looking for an ancestor TableNode. If none exists, the node is not inside a table, so the OrThrow helper throws. This is a structural invariant check: callers require a table ancestor to compute indices or mutate the table.","triggerScenarios":"Calling any $get...FromLexicalNodeOrThrow-style helper with a node that is not a descendant of a TableNode: detached nodes, root-level paragraphs mistakenly assumed to be table content, or cells removed during the same update.","commonSituations":"Running table utilities on selection content that only partially overlaps a table; applying table helpers after table deletion transforms; passing a TableNode's sibling rather than descendant.","solutions":["Guard with the non-throwing $getTableNodeFromLexicalNode first and bail out when it returns null.","Ensure the target node is currently attached under a TableNode before invoking table utilities.","Re-derive the node from the active selection within editor.read/update rather than reusing cached references."],"exampleFix":"// before\nconst table = $getTableNodeFromLexicalNodeOrThrow(someNode);\n// after\nconst table = $getTableNodeFromLexicalNode(someNode);\nif (table === null) return;\n","handlingStrategy":"type-guard","validationCode":"const table = $getTableNodeFromLexicalNode(node);\nif (table === null) return;","typeGuard":"function isInTable(node: LexicalNode): boolean {\n  return $getTableNodeFromLexicalNode(node) !== null;\n}","tryCatchPattern":"try {\n  const table = $getTableNodeFromLexicalNodeOrThrow(node);\n  // ...\n} catch {\n  // node is not inside a table; skip table logic\n}","preventionTips":["Gate table-specific commands on the selection actually intersecting a TableNode","Use non-OrThrow variants ($getTableNodeFromLexicalNode) for optional contexts","Avoid running table helpers on nodes from stale EditorStates"],"tags":["lexical-table","detached-node","hierarchy"],"backgroundTag":"detached-node-hierarchy","analyzedSha":"76a22dcba981b46119fcec45f935270707e26a57","analyzedAt":"2026-08-31T21:45:54.792Z","schemaVersion":2},"datasetVersion":"2026-08-31T22:30:34.772Z"}