{"record":{"id":"b3f7b50c055db3d3","repo":"toeverything/AFFiNE","slug":"doc-not-found-b3f7b5","errorCode":"doc_not_found","errorMessage":"Doc ${docId} under Space ${spaceId} not found.","messagePattern":"Doc (.+?) under Space (.+?) not found\\.","errorType":"exception","errorClass":"DocNotFound","httpStatus":404,"severity":"error","filePath":"packages/backend/server/src/core/workspaces/controller.ts","lineNumber":90,"sourceCode":"    }\n  }\n\n  private async getPublishModeHeader(workspaceId: string, docId: string) {\n    const docMeta = await this.models.doc.getMeta(workspaceId, docId, {\n      select: {\n        mode: true,\n      },\n    });\n    return docMeta?.mode === PublicDocMode.Edgeless\n      ? DocMode.edgeless\n      : DocMode.page;\n  }\n\n  private async getDocBinaryOrThrow(workspaceId: string, docId: string) {\n    const binResponse = await this.docReader.getDoc(workspaceId, docId);\n\n    if (!binResponse) {\n      throw new DocNotFound({\n        spaceId: workspaceId,\n        docId,\n      });\n    }\n\n    return binResponse;\n  }\n\n  @Public()\n  @Get('/:id/blob-manifest/v1')\n  async blobManifestV1(\n    @CurrentUser() user: CurrentUser | undefined,\n    @Param('id') workspaceId: string,\n    @Query('sourceType') sourceType: string,\n    @Query('docId') docId: string,\n    @Query('timestampMs') timestampMs: string | undefined,\n    @Res() res: Response\n  ) {","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/2af30773aecd567f09b346e7b72fc69143144057/packages/backend/server/src/core/workspaces/controller.ts#L72-L108","documentation":"Thrown by WorkspacesController.getDocBinaryOrThrow when DocReader.getDoc(workspaceId, docId) resolves null — after permission checks passed, the server could not load any binary/update data for that doc id in that workspace. It is the not-found signal of the doc storage layer (snapshots/updates plus blob backing).","triggerScenarios":"Internal doc-binary fetches (getDocBinaryOrThrow callers) where the doc was deleted, its snapshots/updates were never written or were garbage-collected, or the guid simply does not exist in that workspace.","commonSituations":"Client holding a stale docId after the doc was deleted in another session; workspace data pruned or migrated without snapshots; copy-pasted wrong guid; snapshot/update storage (S3, database) misconfigured so reads return null; doc created but its first sync never completed.","solutions":["Verify the doc exists via doc meta / workspace doc list before fetching its binary","Check the workspaceId/docId pair — the same guid in the wrong space is not found","If storage was wiped or migrated, restore snapshots/updates or re-create the doc","Server-side, trace DocReader.getDoc dependencies (snapshot + update models, storage) to see which layer returns null for the pair"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// confirm the doc exists before fetching its binary\nconst meta = await getDocMeta(wsId, docId);\nif (!meta) {\n  handleDocGone(docId);\n  return;\n}","typeGuard":"function isDocNotFound(e: unknown): e is { code: 'doc_not_found'; docId: string; spaceId: string } {\n  return typeof e === 'object' && e !== null && (e as any).code === 'doc_not_found';\n}","tryCatchPattern":"try {\n  return await getDocBinary(wsId, docId);\n} catch (e) {\n  if (isDocNotFound(e)) {\n    removeDocFromLocalCache(e.docId); // stale id — stop retrying\n    return null;\n  }\n  throw e;\n}","preventionTips":["Sync doc lists before dereferencing doc ids from old links or caches","Treat doc deletion events as a signal to invalidate stored doc ids","Never guess doc guids or copy them across workspaces","Back up snapshots/updates before migrating storage so reads don't return null"],"tags":["not-found","doc","storage","sync"],"backgroundTag":"resource-not-found","analyzedSha":"2af30773aecd567f09b346e7b72fc69143144057","analyzedAt":"2026-08-18T21:16:52.546Z","contentChangedAt":"2026-08-18T21:16:52.546Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}