{"record":{"id":"14f978ac8368c0ac","repo":"facebook/lexical","slug":"row-before-insertion-index-does-not-exist","errorCode":null,"errorMessage":"Row before insertion index does not exist.","messagePattern":"Row before insertion index does not exist\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/lexical-table/src/LexicalTableUtils.ts","lineNumber":246,"sourceCode":"        ) {\n          headerState |= TableCellHeaderStates.COLUMN;\n        }\n\n        const tableCellNode = $createTableCellNode(headerState, 1, width);\n\n        tableCellNode.append($createParagraphNode());\n\n        newTableRowNode.append(tableCellNode);\n      }\n\n      if (shouldInsertAfter) {\n        targetRowNode.insertAfter(newTableRowNode);\n      } else {\n        targetRowNode.insertBefore(newTableRowNode);\n      }\n    }\n  } else {\n    throw new Error('Row before insertion index does not exist.');\n  }\n\n  return tableNode;\n}\n\nconst getHeaderState = (\n  currentState: TableCellHeaderState,\n  possibleState: TableCellHeaderState,\n): TableCellHeaderState => {\n  if (\n    currentState === TableCellHeaderStates.BOTH ||\n    currentState === possibleState\n  ) {\n    return possibleState;\n  }\n  return TableCellHeaderStates.NO_STATUS;\n};\n","sourceCodeStart":228,"sourceCodeEnd":264,"githubUrl":"https://github.com/facebook/lexical/blob/76a22dcba981b46119fcec45f935270707e26a57/packages/lexical-table/src/LexicalTableUtils.ts#L228-L264","documentation":"In $insertTableRow, after the range check passes, the anchor row (tableRows[targetIndex]) must still be a TableRowNode. If it isn't (e.g. the table has malformed children or an empty/mismatched child list), the else branch throws 'Row before insertion index does not exist.'","triggerScenarios":"targetIndex valid numerically but tableRows[targetIndex] is not a TableRowNode — typically because the table's children were mutated into a non-row node, or targetIndex points at a child removed during the same update, or an empty children array combined with a range-check bypass.","commonSituations":"Custom nodes or transforms injected as direct children of TableNode; collaboration merges that desync the row list; calling insert while a prior transform in the same update removed the target row.","solutions":["Ensure TableNode children are exclusively TableRowNodes; remove any custom children or wrap them elsewhere.","Re-read the anchor row within the same update and verify $isTableRowNode before inserting.","If collaborating, ensure the Yjs table structure stays row-only (SyncV2 schema/registered nodes for tables are correct)."],"exampleFix":"// before\n$insertTableRow(tableNode, idx, true, 1, dom);\n// after\nconst rows = tableNode.getChildren();\nif ($isTableRowNode(rows[idx])) {\n  $insertTableRow(tableNode, idx, true, 1, dom);\n}","handlingStrategy":"validation","validationCode":"const anchor = tableNode.getChildren()[targetIndex];\nif (!$isTableRowNode(anchor)) return; // malformed table, abort","typeGuard":"function rowExistsAt(tableNode: TableNode, idx: number): boolean {\n  return $isTableRowNode(tableNode.getChildren()[idx]);\n}","tryCatchPattern":"try {\n  $insertTableRow(tableNode, idx, true, 1, dom);\n} catch (e) {\n  if (e.message.includes('Row before insertion index')) return;\n  throw e;\n}","preventionTips":["Keep TableNode children exclusively TableRowNodes — never append arbitrary nodes to a TableNode","Verify table structure after collaboration merges","Run a structural sanity check ($isTableRowNode on every child) before bulk table ops"],"tags":["lexical-table","invalid-structure"],"backgroundTag":"invalid-table-structure","analyzedSha":"76a22dcba981b46119fcec45f935270707e26a57","analyzedAt":"2026-08-31T21:45:54.792Z","schemaVersion":2},"datasetVersion":"2026-08-31T22:30:34.772Z"}