toeverything/AFFiNE · error · BlockSuiteError

GfxBlockElementError

GfxBlockElementError

Error message

Frame model is not found

What it means

Thrown by FrameManager._addFrameBlock after gfx.doc.addBlock('affine:frame', ...) when gfx.getElementById(id) returns null or something that fails isFrameBlock. This means the freshly added block either was not committed/registered by the time of lookup, or the flavour/schema for 'affine:frame' is not registered so addBlock produced an unexpected model. Error code GfxBlockElementError.

Source

Thrown at blocksuite/affine/blocks/frame/src/frame-manager.ts:224

    frame.addChildren(childElements);
  }

  private _addFrameBlock(bound: Bound) {
    const surfaceModel = this.gfx.surface as SurfaceBlockModel;
    const props = this.gfx.std
      .get(EditPropsStore)
      .applyLastProps('affine:frame', {
        title: new Text(new Y.Text(`Frame ${this.frames.length + 1}`)),
        xywh: bound.serialize(),
        index: this.gfx.layer.generateIndex(true),
        presentationIndex: this.generatePresentationIndex(),
      });

    const id = this.gfx.doc.addBlock('affine:frame', props, surfaceModel);
    const frameModel = this.gfx.getElementById(id);

    if (!frameModel || !isFrameBlock(frameModel)) {
      throw new BlockSuiteError(
        ErrorCode.GfxBlockElementError,
        'Frame model is not found'
      );
    }

    return frameModel;
  }

  private _watchElementAdded() {
    if (!this.gfx.surface) {
      return;
    }

    const { surface: surfaceModel, doc } = this.gfx;

    this._disposable.add(
      surfaceModel.elementAdded.subscribe(({ id, local }) => {
        const element = surfaceModel.getElementById(id);

View on GitHub (pinned to 26c515e050)

Solutions

  1. Ensure the 'affine:frame' block schema is registered (import the frame block module / add its extension) before the edgeless editor mounts.
  2. Ensure addBlock is called within a proper store context (doc.addBlock with a valid surfaceModel parent) and that the doc is not destroyed mid-call.
  3. If the race is timing-related, defer frame creation until the surface block is available (gfx.surface truthy) — _watchElementAdded already guards on surface presence.

Example fix

// before
// affine:frame schema not registered -> getElementById returns null

// after
import '@blocksuite/affine-block-frame'; // registers the frame schema
// then mount the editor; frame creation will resolve a FrameBlockModel
Defensive patterns

Strategy: validation

Validate before calling

import { isFrameBlock } from '@blocksuite/affine-block-frame';
function tryAddFrame(manager, bound) {
  if (!manager.gfx.surface) return null; // surface not ready
  return manager.addFrame(bound);
}

Type guard

import { isFrameBlock } from '@blocksuite/affine-block-frame';
import type { GfxBlockElementModel } from '@blocksuite/affine-model';
function isFrame(model): model is FrameBlockModel {
  return !!model && isFrameBlock(model);
}

Prevention

When it happens

Trigger: Calling the public add-frame path (FrameManager.addFrame or whatever calls _addFrameBlock) before the affine:frame schema is registered, or when the surface model passed to addBlock is invalid/missing, or when an idGenerator/transaction timing issue causes getElementById to miss the just-added block.

Common situations: Initialising the edgeless surface before all block schemas are registered; calling frame creation during doc load before the surface block is ready; custom builds that tree-shake the frame block; race conditions where addBlock runs inside an uncommitted transaction.

Related errors


AI-assisted analysis of toeverything/AFFiNE@26c515e050 (2026-08-12). Data as JSON: /api/errors/a7ec11917cb7e295. Report an issue: GitHub.