toeverything/AFFiNE · critical · BlockSuiteError

ErrorCode.NoRootModelError

ErrorCode.NoRootModelError

Error message

This doc is missing root block. Please initialize the default block structure before connecting the editor to DOM.

What it means

LitHost.connectedCallback (the <editor-host> root) requires store.root to exist before std.mount(); a doc with no root block has nothing to render, so BlockSuite fails fast with NoRootModelError. It tells you the document's block tree was not initialized (no root flavour like affine:page was ever added) before the editor was attached to the DOM.

Solutions

  1. Initialize the default block structure before connecting: use doc.load(init => init.block('affine:page', ...)) or add the page/root block, then mount the editor.
  2. For lazily loaded docs, wait for the doc content (workspace.doc loaded / doc.loaded await) before appending <editor-host>.
  3. Verify snapshot import actually created a root (check doc.root) and that the root flavour's schema is registered.
  4. Show a loading state until store.root is non-null instead of mounting early.

Example fix

// before
const doc = workspace.createDoc(docId);
document.body.append(editor); // NoRootModelError

// after
const doc = workspace.createDoc(docId);
doc.load(init => init.block('affine:page', {}, init.block('affine:surface', {})))
  .then(() => document.body.append(editor));
Defensive patterns

Strategy: validation

Validate before calling

// Only mount the editor when the doc has a root:
if (doc.root) {
  document.body.append(editor);
} else {
  await doc.load(init => init.block('affine:page', {}));
  document.body.append(editor);
}

Type guard

function docHasRoot(doc: Store): boolean {
  return doc.root != null;
}

Prevention

When it happens

Trigger: Creating a new empty doc (new Doc(...)) and appending the editor host before any root block exists; forgetting doc.load()/init with a default structure; importing a snapshot that failed or produced no root; referencing a doc id that has no data yet.

Common situations: Bootstrapping fresh workspaces without calling the standard init (e.g. doc.load(() => doc.addBlock('affine:page', {}, ...))); lazy-loading docs where the editor mounts before the (async) doc content arrives; snapshot middleware skipping the root node.

Related errors


AI-assisted analysis of toeverything/AFFiNE@b4c8548c09 (2026-08-18). Data as JSON: /api/errors/8ade845278c2bd83. Report an issue: GitHub.

Appendix: source

Thrown at blocksuite/framework/std/src/view/element/lit-host.ts:131

  }

  get range(): RangeManager {
    return this.std.range;
  }

  get selection(): StoreSelectionExtension {
    return this.std.selection;
  }

  get view(): ViewStore {
    return this.std.view;
  }

  override connectedCallback() {
    super.connectedCallback();

    if (!this.store.root) {
      throw new BlockSuiteError(
        ErrorCode.NoRootModelError,
        'This doc is missing root block. Please initialize the default block structure before connecting the editor to DOM.'
      );
    }

    this.std.mount();
    this.tabIndex = 0;
  }

  override disconnectedCallback() {
    super.disconnectedCallback();
    this.std.unmount();
  }

  override async getUpdateComplete(): Promise<boolean> {
    try {
      const result = await super.getUpdateComplete();
      const rootModel = this.store.root;

View on GitHub (pinned to b4c8548c09)