toeverything/AFFiNE · error · BlockSuiteError
ErrorCode.ModelCRUDError
ErrorCode.ModelCRUDError
Error message
cannot modify data in readonly mode
What it means
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.
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
Example fix
// before
doc.readonly = true;
doc.addBlock('affine:paragraph', {}, parentId); // -> cannot modify data in readonly mode
// after
if (!doc.readonly) {
doc.addBlock('affine:paragraph', {}, parentId);
} Defensive patterns
Strategy: validation
Validate before calling
if (!doc.readonly) {
doc.addBlock(flavour, props, parentId);
} else {
// request a writable session or surface a permission notice
} Type guard
const isWritableDoc = (doc: Store): boolean => !doc.readonly;
Try / catch
try {
doc.addBlock(flavour, props, parentId);
} catch (e) {
if (e instanceof BlockSuiteError && e.code === ErrorCode.ModelCRUDError && doc.readonly) {
// open a writable session instead of mutating the readonly one
} else {
throw e;
}
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
AI-assisted analysis of toeverything/AFFiNE@b4c8548c09 (2026-08-18).
Data as JSON: /api/errors/b66c8a79b6030392.
Report an issue: GitHub.
Appendix: source
Thrown at blocksuite/framework/store/src/model/store/store.ts:750
/**
* Creates and adds a new block to the store
* @param flavour - The block's flavour (type)
* @param blockProps - Optional properties for the new block
* @param parent - Optional parent block or parent block ID
* @param parentIndex - Optional index position in parent's children
* @returns The ID of the newly created block
* @throws {BlockSuiteError} When store is in readonly mode
*
* @category Block CRUD
*/
addBlock<T extends BlockModel = BlockModel>(
flavour: string,
blockProps: Partial<(PropsOfModel<T> & BlockSysProps) | BlockProps> = {},
parent?: BlockModel | string | null,
parentIndex?: number
): string {
if (this.readonly) {
throw new BlockSuiteError(
ErrorCode.ModelCRUDError,
'cannot modify data in readonly mode'
);
}
const id = blockProps.id ?? this._doc.workspace.idGenerator();
this.transact(() => {
this._crud.addBlock(
id,
flavour,
{ ...blockProps },
typeof parent === 'string' ? parent : parent?.id,
parentIndex
);
});
return id;View on GitHub (pinned to b4c8548c09)