BookStackApp/BookStack · error · Error
${error.toString()} Parent: ${dom.tagName}, new child: {tag:
Error message
${error.toString()} Parent: ${dom.tagName}, new child: {tag: ${replacementDOM.tagName} key: ${nextFrstChildKey}}, old child: {tag: ${lastDOM.tagName}, key: ${prevFirstChildKey}}. What it means
Lexical's development-mode reconciler ($reconcileChildren) throws this internal invariant when it must destroy the first child DOM node during reconciliation and detects a tag/key mismatch between the existing DOM child and the replacement node — i.e. Lexical's node state and the rendered DOM have diverged. The message embeds the parent tag, new child tag/key, and old child tag/key to help report the invariant violation upstream.
Source
Thrown at resources/js/wysiwyg/lexical/core/LexicalReconciler.ts:378
const prevFirstChildKey = prevElement.__first as NodeKey;
const nextFrstChildKey = nextElement.__first as NodeKey;
if (prevFirstChildKey === nextFrstChildKey) {
$reconcileNode(prevFirstChildKey, dom);
} else {
const lastDOM = getPrevElementByKeyOrThrow(prevFirstChildKey);
const replacementDOM = $createNode(nextFrstChildKey, null, null);
try {
dom.replaceChild(replacementDOM, lastDOM);
} catch (error) {
if (typeof error === 'object' && error != null) {
const msg = `${error.toString()} Parent: ${
dom.tagName
}, new child: {tag: ${
replacementDOM.tagName
} key: ${nextFrstChildKey}}, old child: {tag: ${
lastDOM.tagName
}, key: ${prevFirstChildKey}}.`;
throw new Error(msg);
} else {
throw error;
}
}
destroyNode(prevFirstChildKey, null);
}
const nextChildNode = activeNextNodeMap.get(nextFrstChildKey);
if ($isTextNode(nextChildNode)) {
if (subTreeTextFormat === null) {
subTreeTextFormat = nextChildNode.getFormat();
}
if (subTreeTextStyle === '') {
subTreeTextStyle = nextChildNode.getStyle();
}
}
} else {
const prevChildren = createChildrenArray(prevElement, activePrevNodeMap);
const nextChildren = createChildrenArray(nextElement, activeNextNodeMap);View on GitHub (pinned to 18f8469a1c)
Solutions
- Wrap all node mutations in editor.update() callbacks; never mutate nodes directly from listeners
- Use $applyNodeReplacement / proper node APIs when replacing nodes in custom plugins
- Reproduce in dev build and report to Lexical maintainers with the tag/key info from the message — this is an internal invariant
- Pin/upgrade Lexical and plugins to matching versions
Example fix
// before
node.replace(newNode); // outside editor.update()
// after
editor.update(() => {
newNode.replace(node);
}); Defensive patterns
Strategy: try-catch
Validate before calling
// Ensure all mutations happen inside editor.update()
if (!editor._updateInProgress /* or via wrapper */) {
// refactor: schedule inside update instead of mutating now
editor.update(() => performMutation());
} Type guard
function isInsideLexicalUpdate(fn: () => void): () => void {
return () => editor.update(fn);
} Try / catch
try {
editor.update(() => applyNodeChanges());
} catch (e) {
console.error('Lexical reconciliation invariant violated', e);
// recover: reload editor state from last serialized snapshot
editor.setEditorState(lastGoodState);
} Prevention
- Wrap every node mutation in editor.update()
- Use $applyNodeReplacement for custom node swaps
- Keep Lexical and plugins on matching versions
- Test plugins against dev builds to catch invariants early
When it happens
Trigger: $reconcileChildrenWithDirection encountering a corrupted reconciliation path during $commitPendingUpdates: typically after a plugin mutates nodes outside updateEditor, or a node transformer/listener mutates the tree during reconciliation.
Common situations: Third-party Wysiwyg plugins calling editor APIs outside editor.update(); concurrent mutations from event listeners; Lexical version upgrades with custom plugins relying on old behavior; programmatic node replacement without $applyNodeReplacement.
Related errors
- Cannot call set() on a frozen Lexical node map
- Cannot call clear() on a frozen Lexical node map
- Cannot call delete() on a frozen Lexical node map
- ${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/6fecc38fa70af87f.
Report an issue: GitHub.