toeverything/AFFiNE · critical · BlockSuiteError
ErrorCode.ModelCRUDError
ErrorCode.ModelCRUDError
Error message
schema for flavour: ${this.flavour} not found What it means
When a flat-data-model block is instantiated from its Yjs map, FlatSyncController resolves the block's sys:flavour in Schema.flavourSchemaMap before building the model. If no schema is registered under that flavour, the store cannot create the model and throws this ModelCRUDError during block hydration.
Solutions
- Register every flavour the doc can contain via schema.register([...schemas]) before the doc is loaded
- Inspect the doc's blocks Y.Map and collect all sys:flavour values to find which one is missing from doc.schema.flavourSchemaMap
- If a flavour was renamed, add a migration that rewrites sys:flavour on old blocks or register a legacy schema
- As a last resort for disposable data, delete the offending block from the snapshot and let the doc load
Example fix
// before
const schema = new Schema();
const doc = workspace.createSession({ id: 'doc1', schema });
// doc1 contains 'my:flat' blocks -> schema for flavour: my:flat not found
// after
const schema = new Schema();
schema.register([MyFlatBlock]); // defineBlockSchema({ flavour: 'my:flat', isFlatData: true, ... })
const doc = workspace.createSession({ id: 'doc1', schema }); Defensive patterns
Strategy: validation
Validate before calling
for (const [, yBlock] of doc.getBlockMap()) {
const flavour = yBlock.get('sys:flavour') as string;
if (!doc.schema.flavourSchemaMap.has(flavour)) {
console.warn('unregistered flavour in doc:', flavour);
}
} Type guard
const isFlavourRegistered = (schema: Schema, flavour: string): boolean => schema.flavourSchemaMap.has(flavour);
Try / catch
try {
const doc = workspace.createSession({ id, schema });
} catch (e) {
if (e instanceof BlockSuiteError && e.code === ErrorCode.ModelCRUDError) {
// log flavour registry and doc contents, register missing schemas, retry load
}
throw e;
} Prevention
- Register all block schemas in one place before any doc loads
- Log schema.flavourSchemaMap keys whenever hydration fails to spot the missing flavour fast
- Keep legacy flavour schemas registered when renaming blocks
When it happens
Trigger: Instantiating a Block for a Yjs block map whose sys:flavour is not registered on the Schema instance passed to the Store — e.g. a doc written by another build containing 'my:custom' flavours, or a flavour string that was renamed.
Common situations: Forgetting schema.register([...]) before loading persisted docs; opening documents produced by a different editor configuration; renaming or splitting a block flavour without migrating old docs; schemas lazy-loaded after the doc has already mounted.
Related errors
- ErrorCode.ModelCRUDError
- ErrorCode.ModelCRUDError
- Block cannot have parent
- Schema not found. The block flavour may not be registered.
- Invalid key for
AI-assisted analysis of toeverything/AFFiNE@b4c8548c09 (2026-08-18).
Data as JSON: /api/errors/2f2cddc48e0d08eb.
Report an issue: GitHub.
Appendix: source
Thrown at blocksuite/framework/store/src/model/block/flat-sync-controller.ts:43
readonly schema: Schema,
readonly yBlock: YBlock,
readonly doc?: Store,
readonly onChange?: (key: string, isLocal: boolean) => void
) {
const { id, flavour, version, yChildren, props } = this._parseYBlock();
this.id = id;
this.flavour = flavour;
this.yChildren = yChildren;
this.version = version;
this.model = this._createModel(props);
}
private _createModel(props: Set<string>) {
const schema = this.schema.flavourSchemaMap.get(this.flavour);
if (!schema) {
throw new BlockSuiteError(
ErrorCode.ModelCRUDError,
`schema for flavour: ${this.flavour} not found`
);
}
const model = schema.model.toModel?.() ?? new BlockModel<object>();
const defaultProps = schema.model.props?.(internalPrimitives);
model.schema = schema;
model.id = this.id;
model.keys = Array.from(props);
model.yBlock = this.yBlock;
const reactive = new ReactiveFlatYMap(
this.yBlock,
model.deleted,
this.onChange,
defaultProps
);View on GitHub (pinned to b4c8548c09)