toeverything/AFFiNE · error · BlockSuiteError
ErrorCode.MissingViewModelError
ErrorCode.MissingViewModelError
Error message
Cannot find block model for id ${this.blockId} What it means
Thrown by the `BlockComponent.model` getter (block-component.ts) when `this.store.getModelById(this.blockId)` returns undefined. The component is bound to a `blockId` that does not exist in the store — either it was never created, was deleted, or the store has not loaded it yet. The result is cached, so the lookup happens once per component instance.
Source
Thrown at blocksuite/framework/std/src/view/element/block-component.ts:114
const expectedVersion = schema.version;
const actualVersion = this.model.version;
if (expectedVersion !== actualVersion) {
console.warn(
`Version mismatch for block ${this.model.id}, expected ${expectedVersion}, actual ${actualVersion}`
);
return true;
}
return false;
}
get model() {
if (this._model) {
return this._model;
}
const model = this.store.getModelById<Model>(this.blockId);
if (!model) {
throw new BlockSuiteError(
ErrorCode.MissingViewModelError,
`Cannot find block model for id ${this.blockId}`
);
}
this._model = model;
return model;
}
get parentComponent(): BlockComponent | null {
const parent = this.model.parent;
if (!parent) return null;
return this.std.view.getBlock(parent.id);
}
get renderChildren() {
return this.host.renderChildren.bind(this);
}
View on GitHub (pinned to 26c515e050)
Solutions
- Wait for `store.loaded` (the doc loaded signal/promise) before rendering block components.
- Filter rendered ids through `store.getModelById` first and drop missing ones.
- Re-derive `blockId` lists reactively from the model tree rather than caching stale ids.
- Confirm the same `store` instance is shared by the host and the component.
Example fix
// before: rendering possibly-stale ids
html`${repeat(ids, id => id, id => html`<block-component blockId=${id}></block-component>`)}`
// after: filter to ids that still exist
html`${repeat(ids.filter(id => store.getModelById(id)), id => id, id => html`<block-component blockId=${id}></block-component>`)}` Defensive patterns
Strategy: validation
Validate before calling
// filter block ids through the store before rendering
function existingBlockIds(store: Store, ids: string[]): string[] {
return ids.filter(id => !!store.getModelById(id));
}
const liveIds = existingBlockIds(store, ids);
render(html`${repeat(liveIds, id => id, id => html`<block-component blockId=${id}></block-component>`)}`, container); Type guard
function blockExists(store: Store, id: string): boolean {
return !!store.getModelById(id);
} Try / catch
import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions';
let model: Model;
try {
model = blockComponent.model;
} catch (e) {
if (e instanceof BlockSuiteError && e.code === ErrorCode.MissingViewModelError) {
// block was deleted; skip rendering
return;
}
throw e;
} Prevention
- Derive block id lists reactively from the model tree rather than caching them.
- Wait for `store.loaded` before rendering block components.
- Confirm the same store instance is shared by host and component.
When it happens
Trigger: Rendering a `<block-component blockId="...">` whose id is not present in the store: stale id left in a template after the block was deleted; store still loading asynchronously; id typo; wrong store instance passed to the host.
Common situations: Listing child models then rendering them while a collaborative delete removes one mid-render; SSR with an empty store; passing a `blockId` from one doc into a host bound to another doc; reading `model` in `connectedCallback` before `doc.loaded`.
Related errors
- Blob engine is not initialized
- DatabaseBlockError
- ErrorCode.ValueNotExists
- ErrorCode.DefaultRuntimeError
- ErrorCode.NoRootModelError
AI-assisted analysis of toeverything/AFFiNE@26c515e050 (2026-08-12).
Data as JSON: /api/errors/24a6f0233876db0d.
Report an issue: GitHub.