{"record":{"id":"d38051126fee60ec","repo":"toeverything/AFFiNE","slug":"modelcruderror","errorCode":"ModelCRUDError","errorMessage":"updating block: ${model.id} not found","messagePattern":"updating block: (.+?) not found","errorType":"exception","errorClass":"BlockSuiteError","httpStatus":null,"severity":"error","filePath":"blocksuite/framework/store/src/model/store/store.ts","lineNumber":893,"sourceCode":"    if (!model) {\n      throw new BlockSuiteError(\n        ErrorCode.ModelCRUDError,\n        `updating block: ${modelOrId} not found`\n      );\n    }\n\n    if (!isCallback) {\n      const parent = this.getParent(model);\n      this.schema.validate(\n        model.flavour,\n        parent?.flavour,\n        callBackOrProps.children?.map(child => child.flavour)\n      );\n    }\n\n    const yBlock = this._yBlocks.get(model.id);\n    if (!yBlock) {\n      throw new BlockSuiteError(\n        ErrorCode.ModelCRUDError,\n        `updating block: ${model.id} not found`\n      );\n    }\n\n    const block = this.getBlock(model.id);\n    if (!block) return;\n\n    this.transact(() => {\n      if (isCallback) {\n        callBackOrProps();\n        this._runQuery(block);\n        return;\n      }\n\n      if (callBackOrProps.children) {\n        this._crud.updateBlockChildren(\n          model.id,","sourceCodeStart":875,"sourceCodeEnd":911,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/b4c8548c09da21b2898443559a5b846f0ccf5dd8/blocksuite/framework/store/src/model/store/store.ts#L875-L911","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nconst model = getSomeCachedModel(); // block deleted meanwhile\ndoc.updateBlock(model, { type: 'h1' }); // -> updating block: <id> not found\n\n// after\nconst block = doc.getBlock(model.id);\nif (!block) return;\ndoc.updateBlock(block.model, { type: 'h1' });","handlingStrategy":"validation","validationCode":"// model may be stale even if the object still exists in memory\nconst block = doc.getBlock(model.id);\nif (block) {\n  doc.updateBlock(block.model, props);\n}","typeGuard":"function isLiveModel(doc: Store, model: BlockModel): boolean {\n  return doc.hasBlock(model.id);\n}","tryCatchPattern":"try {\n  doc.updateBlock(model, props);\n} catch (e) {\n  if (e instanceof BlockSuiteError && e.code === ErrorCode.ModelCRUDError) {\n    // remote deletion won the race: discard the edit, refresh from doc.getBlock\n  } else {\n    throw e;\n  }\n}","preventionTips":["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"],"tags":["blocksuite","updateblock","stale-reference","yjs","crud"],"backgroundTag":"entity-not-found","analyzedSha":"b4c8548c09da21b2898443559a5b846f0ccf5dd8","analyzedAt":"2026-08-18T21:16:52.546Z","contentChangedAt":"2026-08-18T21:16:52.546Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}