facebook/lexical · error

Row before insertion index does not exist.

Error message

Row before insertion index does not exist.

What it means

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.'

Source

Thrown at packages/lexical-table/src/LexicalTableUtils.ts:246

        ) {
          headerState |= TableCellHeaderStates.COLUMN;
        }

        const tableCellNode = $createTableCellNode(headerState, 1, width);

        tableCellNode.append($createParagraphNode());

        newTableRowNode.append(tableCellNode);
      }

      if (shouldInsertAfter) {
        targetRowNode.insertAfter(newTableRowNode);
      } else {
        targetRowNode.insertBefore(newTableRowNode);
      }
    }
  } else {
    throw new Error('Row before insertion index does not exist.');
  }

  return tableNode;
}

const getHeaderState = (
  currentState: TableCellHeaderState,
  possibleState: TableCellHeaderState,
): TableCellHeaderState => {
  if (
    currentState === TableCellHeaderStates.BOTH ||
    currentState === possibleState
  ) {
    return possibleState;
  }
  return TableCellHeaderStates.NO_STATUS;
};

View on GitHub (pinned to 76a22dcba9)

Solutions

  1. Ensure TableNode children are exclusively TableRowNodes; remove any custom children or wrap them elsewhere.
  2. Re-read the anchor row within the same update and verify $isTableRowNode before inserting.
  3. If collaborating, ensure the Yjs table structure stays row-only (SyncV2 schema/registered nodes for tables are correct).

Example fix

// before
$insertTableRow(tableNode, idx, true, 1, dom);
// after
const rows = tableNode.getChildren();
if ($isTableRowNode(rows[idx])) {
  $insertTableRow(tableNode, idx, true, 1, dom);
}
Defensive patterns

Strategy: validation

Validate before calling

const anchor = tableNode.getChildren()[targetIndex];
if (!$isTableRowNode(anchor)) return; // malformed table, abort

Type guard

function rowExistsAt(tableNode: TableNode, idx: number): boolean {
  return $isTableRowNode(tableNode.getChildren()[idx]);
}

Try / catch

try {
  $insertTableRow(tableNode, idx, true, 1, dom);
} catch (e) {
  if (e.message.includes('Row before insertion index')) return;
  throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of facebook/lexical@76a22dcba9 (2026-08-31). Data as JSON: /api/errors/14f978ac8368c0ac. Report an issue: GitHub.