toeverything/AFFiNE · error · BlockSuiteError

ErrorCode.ModelCRUDError

ErrorCode.ModelCRUDError

Error message

schema for flavour: ${flavour} not found

What it means

Thrown by DocCRUD.addBlock: the flavour passed in is not present in schema.flavourSchemaMap. This is the public write-path equivalent of the parse-path schema errors; addBlock needs the schema to compute the version, default props, and validate parent/child rules, so an unknown flavour is rejected before any Yjs mutation.

Source

Thrown at blocksuite/framework/store/src/model/store/crud.ts:55

    if (!parent) return null;

    const children = parent.get('sys:children');
    const index = children.toArray().indexOf(id);
    if (index === -1) return null;

    return fn(index, parent);
  }

  addBlock(
    id: string,
    flavour: string,
    initialProps: Record<string, unknown> = {},
    parent?: string | null,
    parentIndex?: number
  ) {
    const schema = this._schema.flavourSchemaMap.get(flavour);
    if (!schema) {
      throw new BlockSuiteError(
        ErrorCode.ModelCRUDError,
        `schema for flavour: ${flavour} not found`
      );
    }

    const hasBlock = this._yBlocks.has(id);
    if (hasBlock) {
      throw new BlockSuiteError(
        ErrorCode.ModelCRUDError,
        `Should not add existing block: ${id}`
      );
    }

    const parentFlavour = parent
      ? this._yBlocks.get(parent)?.get('sys:flavour')
      : undefined;

    this._schema.validate(flavour, parentFlavour as string);

View on GitHub (pinned to 26c515e050)

Solutions

  1. Register the flavour's schema before calling addBlock.
  2. Pull flavour constants from the block package's exports instead of hard-coding strings.
  3. Add a schema.has(flavour) precondition check (or unit test) at call sites that accept dynamic flavours.
  4. If the flavour was renamed, migrate callers or add an alias in the schema.

Example fix

// before
store.addBlock('affine:list', { type: 'bulleted' }, parent);

// after: import the flavour constant
import { ListBlockSchema } from '@blocksuite/affine/blocks/list';
schema.register([ListBlockSchema]);
store.addBlock(ListBlockSchema.model.flavour, { type: 'bulleted' }, parent);
Defensive patterns

Strategy: type-guard

Validate before calling

export function assertCanAddBlock(store: Store, flavour: string): void {
  if (!store.schema.flavourSchemaMap.has(flavour)) {
    throw new Error(`Cannot addBlock: flavour '${flavour}' not registered`);
  }
}

assertCanAddBlock(store, 'affine:paragraph');
store.addBlock('affine:paragraph', {}, parent);

Type guard

export const isAddableFlavour = (store: Store, flavour: string): boolean =>
  store.schema.flavourSchemaMap.has(flavour);

Try / catch

try {
  store.addBlock(flavour, props, parent);
} catch (e) {
  if (e instanceof BlockSuiteError && e.code === ErrorCode.ModelCRUDError && /schema for flavour/.test(e.message)) {
    disableUiForFlavour(flavour); // or register then retry
  } else throw e;
}

Prevention

When it happens

Trigger: Calling store.addBlock('affine:foo', ...) where 'affine:foo' was never registered; passing a typo'd or stale flavour string; using a flavour from a different BlockSuite major version.

Common situations: Hard-coded flavour strings that drift from the registered schema; feature-flagged block not registered in the current build; copy-paste of a flavour literal that has since been renamed; SSR build omitting client-only block modules.

Related errors


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