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
- Delete/remove nodes via editor.update() and $getNodeByKey(...).remove(), never by deleting map entries
- Treat EditorState and its _nodeMap as strictly read-only
- Audit and remove direct _nodeMap access from plugins and utilities
- 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
- Delete nodes via $getNodeByKey(key).remove() inside editor.update()
- Never manipulate EditorState._nodeMap directly
- Keep plugin code free of Lexical internals
- Catch and log dev-only invariant throws with stack traces to find offenders
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
- Cannot call set() on a frozen Lexical node map
- Cannot call clear() on a frozen Lexical node map
- ${error.toString()} Parent: ${dom.tagName}, new child: {tag:
- ${method} is not supported in headless mode
- To use $generateHtmlFromNodes in headless mode please initia
AI-assisted analysis of BookStackApp/BookStack@18f8469a1c (2026-09-02).
Data as JSON: /api/errors/1a4c8e18262b2db6.
Report an issue: GitHub.