{"record":{"id":"b66c8a79b6030392","repo":"toeverything/AFFiNE","slug":"errorcode-modelcruderror-b66c8a","errorCode":"ErrorCode.ModelCRUDError","errorMessage":"cannot modify data in readonly mode","messagePattern":"cannot modify data in readonly mode","errorType":"exception","errorClass":"BlockSuiteError","httpStatus":null,"severity":"error","filePath":"blocksuite/framework/store/src/model/store/store.ts","lineNumber":750,"sourceCode":"  /**\n   * Creates and adds a new block to the store\n   * @param flavour - The block's flavour (type)\n   * @param blockProps - Optional properties for the new block\n   * @param parent - Optional parent block or parent block ID\n   * @param parentIndex - Optional index position in parent's children\n   * @returns The ID of the newly created block\n   * @throws {BlockSuiteError} When store is in readonly mode\n   *\n   * @category Block CRUD\n   */\n  addBlock<T extends BlockModel = BlockModel>(\n    flavour: string,\n    blockProps: Partial<(PropsOfModel<T> & BlockSysProps) | BlockProps> = {},\n    parent?: BlockModel | string | null,\n    parentIndex?: number\n  ): string {\n    if (this.readonly) {\n      throw new BlockSuiteError(\n        ErrorCode.ModelCRUDError,\n        'cannot modify data in readonly mode'\n      );\n    }\n\n    const id = blockProps.id ?? this._doc.workspace.idGenerator();\n\n    this.transact(() => {\n      this._crud.addBlock(\n        id,\n        flavour,\n        { ...blockProps },\n        typeof parent === 'string' ? parent : parent?.id,\n        parentIndex\n      );\n    });\n\n    return id;","sourceCodeStart":732,"sourceCodeEnd":768,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/b4c8548c09da21b2898443559a5b846f0ccf5dd8/blocksuite/framework/store/src/model/store/store.ts#L732-L768","documentation":"Store.addBlock is the only mutating API that hard-throws in readonly mode (updateBlock/deleteBlock/moveBlocks only console.error and return). A Store opened with readonly: true via the StoreContainer session options, or after setting doc.readonly = true, rejects every addBlock call with this ModelCRUDError to protect the shared Yjs doc.","triggerScenarios":"doc.addBlock(...) on a store created with createSession({ id, readonly: true }) or after doc.readonly = true — common in preview/embed/reader modes or shared publication views.","commonSituations":"Editor mounted in read-only embed mode but edit toolbar still enabled; restoring a doc from a public share link where the session is readonly; stale component state pointing at a readonly session after a permission change.","solutions":["Check doc.readonly (or subscribe to doc.readonly$) and disable/gate editing UI before calling addBlock","If editing is intended, create the session without readonly or set doc.readonly = false first","Audit components reused between reader and editor modes so mutations are conditional"],"exampleFix":"// before\ndoc.readonly = true;\ndoc.addBlock('affine:paragraph', {}, parentId); // -> cannot modify data in readonly mode\n\n// after\nif (!doc.readonly) {\n  doc.addBlock('affine:paragraph', {}, parentId);\n}","handlingStrategy":"validation","validationCode":"if (!doc.readonly) {\n  doc.addBlock(flavour, props, parentId);\n} else {\n  // request a writable session or surface a permission notice\n}","typeGuard":"const isWritableDoc = (doc: Store): boolean => !doc.readonly;","tryCatchPattern":"try {\n  doc.addBlock(flavour, props, parentId);\n} catch (e) {\n  if (e instanceof BlockSuiteError && e.code === ErrorCode.ModelCRUDError && doc.readonly) {\n    // open a writable session instead of mutating the readonly one\n  } else {\n    throw e;\n  }\n}","preventionTips":["Bind editing UI disabled-state to doc.readonly$ so it reacts to mode changes","Remember addBlock throws while update/delete only console.error — gate all of them","Pass explicit readonly only for genuine viewer sessions"],"tags":["blocksuite","readonly","addblock","permissions","crud"],"backgroundTag":"read-only-mode-violation","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"}