facebook/lexical · error

$createTextNodesFromYText: Node ${type} is not registered

Error message

$createTextNodesFromYText: Node ${type} is not registered

What it means

During Yjs→Lexical sync (SyncV2), a Y.Text node's stored node type string is looked up in the editor's registered node map (editor._nodes). If no class is registered under that type, Lexical cannot instantiate the corresponding LexicalNode and throws instead of silently dropping data. This guarantees collaborative content never disappears due to a missing registration.

Source

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

): TextNode[] | null => {
  const deltas = toDelta(text, snapshot, prevSnapshot, computeYChange);

  // Use existing text nodes if the count and types all align, otherwise throw out the existing
  // nodes and create new ones.
  let nodes: TextNode[] = binding.mapping.get(text) ?? [];

  const nodeTypes: string[] = deltas.map(
    delta => delta.attributes.t ?? TextNode.getType(),
  );
  const canReuseNodes =
    nodes.length === nodeTypes.length &&
    nodes.every((node, i) => node.getType() === nodeTypes[i]);
  if (!canReuseNodes) {
    const registeredNodes = binding.editor._nodes;
    nodes = nodeTypes.map(type => {
      const nodeInfo = registeredNodes.get(type);
      if (nodeInfo === undefined) {
        throw new Error(
          `$createTextNodesFromYText: Node ${type} is not registered`,
        );
      }
      const node = new nodeInfo.klass();
      if (!$isTextNode(node)) {
        throw new Error(
          `$createTextNodesFromYText: Node ${type} is not a TextNode`,
        );
      }
      return node;
    });
  }

  // Sync text, properties and state to the text nodes.
  for (let i = 0; i < deltas.length; i++) {
    const node = nodes[i];
    const delta = deltas[i];
    const {attributes, insert} = delta;

View on GitHub (pinned to 76a22dcba9)

Solutions

  1. Register every node type that can appear in the shared Yjs document: add the class to the editor's nodes config / extension nodes: array.
  2. Verify the exact missing type from the error message and ensure both ends of the collaboration use the same package/version that defines it.
  3. If the type is a built-in (e.g. 'text', 'hashtag'), import and register the corresponding package's nodes.
  4. For schema-drift between clients, gate sync or render a placeholder node while the type is unavailable.

Example fix

// before
const initialConfig = { nodes: [RootNode, ParagraphNode] };
// after
const initialConfig = { nodes: [RootNode, ParagraphNode, TextNode, HashtagNode] };
Defensive patterns

Strategy: validation

Validate before calling

const type = /* type from Y.Text */;
const registered = editor._nodes.get(type);
if (!registered) {
  throw new Error(`Editor missing registration for Yjs node type: ${type}`);
}

Type guard

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

Try / catch

try {
  syncYTextToLexical(binding, yText);
} catch (e) {
  if (e instanceof Error && e.message.includes('is not registered')) {
    console.error('Collab schema mismatch: register missing node type', e.message);
  } else { throw e; }
}

Prevention

When it happens

Trigger: $createOrUpdateTextNodesFromYText reads a Y.Text (or nested Y type) whose type attribute names a node type not present in binding.editor._nodes — i.e. the editor was built without that node in its nodes: config.

Common situations: Collab document created in an app that registers custom text nodes (e.g. hashtag or mention nodes) is opened by a client whose editor config omits those nodes; adding a new custom node type on one client and failing to register it on others; reusing a plain-text editor config with a rich-text Yjs doc.

Related errors


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