{"record":{"id":"a69fc098ceb13816","repo":"toeverything/AFFiNE","slug":"main-peer-is-readonly","errorCode":null,"errorMessage":"main peer is readonly","messagePattern":"main peer is readonly","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"blocksuite/framework/sync/src/blob/engine.ts","lineNumber":65,"sourceCode":"    const blobIdSet = new Set<string>();\n\n    for (const source of this.sources) {\n      const blobs = await source.list();\n      for (const blob of blobs) {\n        blobIdSet.add(blob);\n      }\n    }\n\n    return Array.from(blobIdSet);\n  }\n\n  async set(value: Blob): Promise<string>;\n\n  async set(key: string, value: Blob): Promise<string>;\n\n  async set(valueOrKey: string | Blob, _value?: Blob) {\n    if (this.main.readonly) {\n      throw new Error('main peer is readonly');\n    }\n\n    const key =\n      typeof valueOrKey === 'string'\n        ? valueOrKey\n        : await sha(await valueOrKey.arrayBuffer());\n    const value = typeof valueOrKey === 'string' ? _value : valueOrKey;\n\n    if (!value) {\n      throw new Error('value is empty');\n    }\n\n    // await upload to the main peer\n    await this.main.set(key, value);\n\n    // uploads to other peers in the background\n    Promise.allSettled(\n      this.shadows","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/b4c8548c09da21b2898443559a5b846f0ccf5dd8/blocksuite/framework/sync/src/blob/engine.ts#L47-L83","documentation":"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.","triggerScenarios":"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.","commonSituations":"Opening shared or published docs read-only and then attempting an upload; permission downgrade between open and write; readonly fixture storages in tests.","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."],"exampleFix":"// before\nawait engine.set(blob);\n\n// after\nif (engine.main.readonly) {\n  throw new Error('blob storage is read-only; open with write access to upload');\n}\nawait engine.set(blob);","handlingStrategy":"validation","validationCode":"if (engine.main.readonly) {\n  throw new Error('blob storage opened read-only; cannot write blobs');\n}\nawait engine.set(blob);","typeGuard":"const isWritableBlobEngine = (engine: BlobEngine): boolean =>\n  !engine.main.readonly;","tryCatchPattern":"try {\n  await engine.set(blob);\n} catch (e) {\n  if (e instanceof Error && e.message === 'main peer is readonly') {\n    // open a writable collection/engine instance, then retry\n  } else throw e;\n}","preventionTips":["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."],"tags":["blob","sync","readonly","permissions","blocksuite"],"backgroundTag":"write-to-readonly-storage","analyzedSha":"b4c8548c09da21b2898443559a5b846f0ccf5dd8","analyzedAt":"2026-08-18T21:16:52.546Z","contentChangedAt":"2026-08-18T21:16:52.546Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}