facebook/lexical · error

$createOrUpdateNodeFromYElement: Node ${type} is not registe

Error message

$createOrUpdateNodeFromYElement: Node ${type} is not registered

What it means

SyncV2's $createOrUpdateNodeFromYElement resolves the Y element's nodeName (or RootNode for the root) against binding.editor._nodes, the editor's registered node map. An unregistered type means the shared document contains a node type this editor doesn't know, so the binding cannot instantiate it and throws.

Source

Thrown at packages/lexical-yjs/src/SyncV2.ts:109

export const $createOrUpdateNodeFromYElement = (
  el: XmlElement,
  binding: BindingV2,
  keysChanged: Set<string> | null,
  childListChanged: boolean,
  snapshot?: Snapshot,
  prevSnapshot?: Snapshot,
  computeYChange?: ComputeYChange,
): LexicalNode | null => {
  let node = binding.mapping.get(el);
  if (node && keysChanged && keysChanged.size === 0 && !childListChanged) {
    return node;
  }

  const type = isRootElement(el) ? RootNode.getType() : el.nodeName;
  const registeredNodes = binding.editor._nodes;
  const nodeInfo = registeredNodes.get(type);
  if (nodeInfo === undefined) {
    throw new Error(
      `$createOrUpdateNodeFromYElement: Node ${type} is not registered`,
    );
  }

  if (!node) {
    node = new nodeInfo.klass();
    keysChanged = null;
    childListChanged = true;
  }

  if (childListChanged && node instanceof ElementNode) {
    const children: LexicalNode[] = [];
    const $createChildren = (childType: XmlElement | XmlText | XmlHook) => {
      if (childType instanceof XmlElement) {
        const n = $createOrUpdateNodeFromYElement(
          childType,
          binding,
          new Set(),

View on GitHub (pinned to 76a22dcba9)

Solutions

  1. Register the missing node class in the editor's nodes array (or extension nodes: () => [MyNode]) so its getType() matches the Y element nodeName.
  2. Ensure all collaboration clients run versions that include the same set of custom nodes.
  3. Check for typos/mismatches between the Y.XmlElement nodeName and the Lexical node's getType().
  4. If the node is optional, strip or migrate unknown Y elements before syncing instead of crashing.

Example fix

// before
const editor = createHeadlessEditor({nodes: [RootNode, TextNode] /* ... */});
// after
const editor = createHeadlessEditor({
  nodes: [...DEFAULT_NODES, ImageNode, PageBreakNode],
});
Defensive patterns

Strategy: validation

Validate before calling

editor.read(() => {
  const missing = allDocTypes.filter(t =>
    t !== 'root' && !editor.hasNodes([/* map t to class */]));
  if (missing.length) throw new Error('Unregistered shared node types: ' + missing);
});

Type guard

function hasRegisteredNode(editor: LexicalEditor, type: string): boolean {
  return editor._nodes.has(type);
}

Try / catch

try {
  createBinding(editor, provider, root);
} catch (e) {
  if (e.message.includes('is not registered')) {
    // upgrade editor config / block sync until nodes are registered
  } else throw e;
}

Prevention

When it happens

Trigger: A collaborating client inserted a custom node (e.g. an ImageNode or custom embed) that this editor never registered via nodes: [...]; nodeName mismatches between the serialized type string and the registered class's getType(); connecting a barebones editor to a document produced by the full playground editor.

Common situations: Collaborators running different app versions where one registered a new custom node; headless/SSR editors missing feature packages; copying serialized editor state between apps with different node registries; typos in node type names when registering via XML elementName mapping.

Related errors


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