facebook/lexical · error
node name mismatch!
Error message
node name mismatch!
What it means
When mapping a Yjs XmlElement back to a Lexical node, SyncV2 checks that the Yjs element's nodeName equals the Lexical node's getType() (allowing the root special case). A mismatch means the shared document's structure disagrees with the Lexical node it is being bound to, which would corrupt the mapping, so it throws.
Source
Thrown at packages/lexical-yjs/src/SyncV2.ts:1003
for (const [stateConfig, v] of known) {
attrs[stateKeyToAttrKey(stateConfig.key)] = stateConfig.unparse(v);
}
return attrs;
};
export const $updateYFragment = (
y: YDoc,
yDomFragment: XmlElement,
node: LexicalNode,
binding: BindingV2,
dirtyElements: Set<NodeKey>,
) => {
if (
yDomFragment instanceof XmlElement &&
yDomFragment.nodeName !== node.getType() &&
!(isRootElement(yDomFragment) && node.getType() === RootNode.getType())
) {
throw new Error('node name mismatch!');
}
binding.mapping.set(yDomFragment, node);
// update attributes
if (yDomFragment instanceof XmlElement) {
const yDomAttrs = yDomFragment.getAttributes();
const lexicalAttrs = {
...propertiesToAttributes(node, binding),
...stateToAttributes(node),
};
for (const key in lexicalAttrs) {
if (lexicalAttrs[key] != null) {
const isEqual =
yDomAttrs[key] === lexicalAttrs[key] ||
// deep equality check so we don't sync properties/state with object values every update
(isObject(yDomAttrs[key]) &&
isObject(lexicalAttrs[key]) &&
equalAttrs(yDomAttrs[key], lexicalAttrs[key]));
if (!isEqual && key !== 'ychange') {View on GitHub (pinned to 76a22dcba9)
Solutions
- Align node getType() strings with the Yjs element names present in the shared document.
- Migrate the Yjs document's element names if the Lexical type was renamed.
- Ensure all collaborators use identical node definitions/versions (lock package versions across clients).
- Verify you are passing the matching Lexical node for each XmlElement rather than an arbitrary node.
Example fix
// before
class H1Node { getType() { return 'h1'; } } // Yjs doc has nodeName 'heading'
// after
class H1Node { getType() { return 'heading'; } } // matches Yjs nodeName Defensive patterns
Strategy: validation
Validate before calling
if (yDomFragment instanceof XmlElement &&
yDomFragment.nodeName !== node.getType() &&
!(isRootElement(yDomFragment) && node.getType() === RootNode.getType())) {
throw new Error(`Schema mismatch: yjs=${yDomFragment.nodeName} lexical=${node.getType()}`);
} Type guard
function yjsNameMatches(yEl: unknown, node: LexicalNode): boolean {
return yEl instanceof XmlElement && yEl.nodeName === node.getType();
} Try / catch
try {
bindNode(binding, yDomFragment, node);
} catch (e) {
if (e instanceof Error && e.message === 'node name mismatch!') {
console.error('Yjs/Lexical schema mismatch', {
yjs: (yDomFragment as XmlElement).nodeName,
});
} else { throw e; }
} Prevention
- Keep getType() strings stable; treat renames as document migrations.
- Use identical node definitions and versions across collaborators.
- Validate Yjs document names against the Lexical schema at sync start.
When it happens
Trigger: $bindNodeToYjs (the binding function around line 1003) receives a yDomFragment that is an XmlElement whose nodeName differs from node.getType() and is not the root element mapped to RootNode.
Common situations: Two clients with different node schemas collaborating: one writes <heading> where the other expects 'paragraph'; custom nodes renamed via getType() after documents were created; manually constructing Yjs XML with names that don't match Lexical types.
Related errors
- Unexpected delta format
- $createOrUpdateNodeFromYElement: Node ${type} is not registe
- $createTextNodesFromYText: Node ${type} is not registered
- $createTextNodesFromYText: Node ${type} is not a TextNode
- Invalid state key: ${key}
AI-assisted analysis of facebook/lexical@76a22dcba9 (2026-08-31).
Data as JSON: /api/errors/d2b702cc47f815c2.
Report an issue: GitHub.