BookStackApp/BookStack · error · Error

Cannot call delete() on a frozen Lexical node map

Error message

Cannot call delete() on a frozen Lexical node map

What it means

The third frozen node-map stub: dev builds throw 'Cannot call delete() on a frozen Lexical node map' when .delete() is invoked on the pending editor state's node map. Lexical freezes set/clear/delete to guarantee that pending/committed editor states are never mutated after creation.

Source

Thrown at resources/js/wysiwyg/lexical/core/LexicalUpdates.ts:473

}

function handleDEVOnlyPendingUpdateGuarantees(
  pendingEditorState: EditorState,
): void {
  // Given we can't Object.freeze the nodeMap as it's a Map,
  // we instead replace its set, clear and delete methods.
  const nodeMap = pendingEditorState._nodeMap;

  nodeMap.set = () => {
    throw new Error('Cannot call set() on a frozen Lexical node map');
  };

  nodeMap.clear = () => {
    throw new Error('Cannot call clear() on a frozen Lexical node map');
  };

  nodeMap.delete = () => {
    throw new Error('Cannot call delete() on a frozen Lexical node map');
  };
}

export function $commitPendingUpdates(
  editor: LexicalEditor,
  recoveryEditorState?: EditorState,
): void {
  const pendingEditorState = editor._pendingEditorState;
  const rootElement = editor._rootElement;
  const shouldSkipDOM = editor._headless || rootElement === null;

  if (pendingEditorState === null) {
    return;
  }

  // ======
  // Reconciliation has started.
  // ======

View on GitHub (pinned to 18f8469a1c)

Solutions

  1. Delete/remove nodes via editor.update() and $getNodeByKey(...).remove(), never by deleting map entries
  2. Treat EditorState and its _nodeMap as strictly read-only
  3. Audit and remove direct _nodeMap access from plugins and utilities
  4. Report to Lexical if the throw originates from core/plugin code you don't control

Example fix

// before
editorState._nodeMap.delete(key);
// after
editor.update(() => {
  $getNodeByKey(key)?.remove();
});
Defensive patterns

Strategy: try-catch

Validate before calling

// Remove nodes through the editor, not the map
editor.update(() => {
  const node = $getNodeByKey(key);
  if (node) node.remove();
});

Try / catch

try {
  removeNodeByKey(editor, key);
} catch (e) {
  if (String(e.message).includes('frozen Lexical node map')) {
    console.error('Use editor.update + node.remove(), not _nodeMap.delete');
  }
  throw e;
}

Prevention

When it happens

Trigger: Dev-mode code calling nodeMap.delete(key) on a frozen pendingEditorState._nodeMap, typically from code holding a reference captured before $commitPendingUpdates froze the map.

Common situations: Plugins attempting to remove nodes by deleting map entries directly; custom undo/redo or state-pruning logic touching internals; dev-only (absent in production).

Related errors


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