toeverything/AFFiNE · error · Error
main peer is readonly
Error message
main peer is readonly
What it means
BlobEngine.set() refuses every write when this.main.readonly is true — the primary blob peer was constructed read-only (for example the collection/blob engine was opened without write intent or on read-only storage). All blob writes must go to a writable main peer, so this error is an access-mode problem, not a data problem.
Source
Thrown at blocksuite/framework/sync/src/blob/engine.ts:65
const blobIdSet = new Set<string>();
for (const source of this.sources) {
const blobs = await source.list();
for (const blob of blobs) {
blobIdSet.add(blob);
}
}
return Array.from(blobIdSet);
}
async set(value: Blob): Promise<string>;
async set(key: string, value: Blob): Promise<string>;
async set(valueOrKey: string | Blob, _value?: Blob) {
if (this.main.readonly) {
throw new Error('main peer is readonly');
}
const key =
typeof valueOrKey === 'string'
? valueOrKey
: await sha(await valueOrKey.arrayBuffer());
const value = typeof valueOrKey === 'string' ? _value : valueOrKey;
if (!value) {
throw new Error('value is empty');
}
// await upload to the main peer
await this.main.set(key, value);
// uploads to other peers in the background
Promise.allSettled(
this.shadowsView on GitHub (pinned to b4c8548c09)
Solutions
- Open the collection/blob peer with write access before uploading blobs.
- Guard with if (engine.main.readonly) before calling set and surface a permission error.
- Route writes to a writable engine instance rather than the read-only one.
Example fix
// before
await engine.set(blob);
// after
if (engine.main.readonly) {
throw new Error('blob storage is read-only; open with write access to upload');
}
await engine.set(blob); Defensive patterns
Strategy: validation
Validate before calling
if (engine.main.readonly) {
throw new Error('blob storage opened read-only; cannot write blobs');
}
await engine.set(blob); Type guard
const isWritableBlobEngine = (engine: BlobEngine): boolean => !engine.main.readonly;
Try / catch
try {
await engine.set(blob);
} catch (e) {
if (e instanceof Error && e.message === 'main peer is readonly') {
// open a writable collection/engine instance, then retry
} else throw e;
} Prevention
- Check engine.main.readonly before enabling upload UI or calling set.
- Open collections with write intent when uploads are expected; keep read-only mounts for viewers only.
- Surface permission state to the app so users cannot reach write actions on read-only docs.
When it happens
Trigger: engine.set(key, blob) or engine.set(blob) while the main blob peer was created with readonly: true; referencing a doc/workspace opened read-only (no write permission); test setups mounting blob storage in read-only mode.
Common situations: Opening shared or published docs read-only and then attempting an upload; permission downgrade between open and write; readonly fixture storages in tests.
Related errors
- ErrorCode.ModelCRUDError
- ErrorCode.TransformerError
- ErrorCode.InlineEditorError
- ErrorCode.ValueNotExists
- a should be smaller than b
AI-assisted analysis of toeverything/AFFiNE@b4c8548c09 (2026-08-18).
Data as JSON: /api/errors/a69fc098ceb13816.
Report an issue: GitHub.