toeverything/AFFiNE · error · BlockSuiteError
ModelCRUDError
ModelCRUDError
Error message
updating block: ${model.id} not found What it means
After resolving the model, updateBlock fetches the underlying Yjs map with _yBlocks.get(model.id). A model object that survives in memory while its Y data is gone (deleted block, detached model, concurrent remote delete) produces this ModelCRUDError variant — the model exists but the backing store entry does not.
Solutions
- Treat deleteBlock as invalidating all model references — re-fetch with doc.getBlock(model.id) before every update
- Guard updates with the Y map existence check or wrap updateBlock in try/catch for BlockSuiteError
- Dispose UI state bound to a model when its deleted subject fires
Example fix
// before
const model = getSomeCachedModel(); // block deleted meanwhile
doc.updateBlock(model, { type: 'h1' }); // -> updating block: <id> not found
// after
const block = doc.getBlock(model.id);
if (!block) return;
doc.updateBlock(block.model, { type: 'h1' }); Defensive patterns
Strategy: validation
Validate before calling
// model may be stale even if the object still exists in memory
const block = doc.getBlock(model.id);
if (block) {
doc.updateBlock(block.model, props);
} Type guard
function isLiveModel(doc: Store, model: BlockModel): boolean {
return doc.hasBlock(model.id);
} Try / catch
try {
doc.updateBlock(model, props);
} catch (e) {
if (e instanceof BlockSuiteError && e.code === ErrorCode.ModelCRUDError) {
// remote deletion won the race: discard the edit, refresh from doc.getBlock
} else {
throw e;
}
} Prevention
- Consider a BlockModel reference dead once its deleted subject fires
- For concurrent editors, re-validate liveness (doc.hasBlock) right before each write
- Dispose editor toolbars/menus bound to a model on deletion
When it happens
Trigger: Holding a BlockModel after doc.deleteBlock(model) and calling doc.updateBlock(model, ...); a remote peer deleting the block between your read and your write; models captured by UI components that outlive their block.
Common situations: Event handlers firing after block deletion (e.g. blur/format toolbar on a removed block); undo/redo races; component trees not re-rendering after deletion.
Understand the failure class
Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.
Related errors
- ErrorCode.MissingViewModelError
- ErrorCode.ModelCRUDError
- ReactiveProxyError
- ReactiveProxyError
- ReactiveProxyError
AI-assisted analysis of toeverything/AFFiNE@b4c8548c09 (2026-08-18).
Data as JSON: /api/errors/d38051126fee60ec.
Report an issue: GitHub.
Appendix: source
Thrown at blocksuite/framework/store/src/model/store/store.ts:893
if (!model) {
throw new BlockSuiteError(
ErrorCode.ModelCRUDError,
`updating block: ${modelOrId} not found`
);
}
if (!isCallback) {
const parent = this.getParent(model);
this.schema.validate(
model.flavour,
parent?.flavour,
callBackOrProps.children?.map(child => child.flavour)
);
}
const yBlock = this._yBlocks.get(model.id);
if (!yBlock) {
throw new BlockSuiteError(
ErrorCode.ModelCRUDError,
`updating block: ${model.id} not found`
);
}
const block = this.getBlock(model.id);
if (!block) return;
this.transact(() => {
if (isCallback) {
callBackOrProps();
this._runQuery(block);
return;
}
if (callBackOrProps.children) {
this._crud.updateBlockChildren(
model.id,View on GitHub (pinned to b4c8548c09)