{"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":88,"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  // get workspace blob\n  //\n  // NOTE: because graphql can't represent a File, so we have to use REST API to get blob\n  @Public()\n  @Get('/:id/blobs/:name')\n  @CallMetric('controllers', 'workspace_get_blob')\n  async blob(\n    @CurrentUser() user: CurrentUser | undefined,\n    @Param('id') workspaceId: string,\n    @Param('name') name: string,","sourceCodeStart":70,"sourceCodeEnd":106,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/26c515e050211269e911f7d9cfe162a26c83ed98/packages/backend/server/src/core/workspaces/controller.ts#L70-L106","documentation":"Thrown by getDocBinaryOrThrow when DocReader.getDoc returns a falsy binary response for the requested workspace/doc. It means the doc snapshot/updater has no binary for that id: the doc does not exist in this workspace or its data has not been replicated yet.","triggerScenarios":"Calling the doc-fetch endpoint for a docId that was never created, was deleted, belongs to a different workspace, or whose binary is not yet available on this node (replication lag).","commonSituations":"Stale bookmark/URL to a deleted doc; cross-workspace id collision; a freshly created doc whose binary has not synced; pointing at the wrong workspace id.","solutions":["Confirm the docId and workspaceId are correct and that the doc exists in that workspace.","If recently created/deleted, retry after replication settles.","Check DocReader/snapshot storage health if many docs 404 unexpectedly.","Clean up stale references/bookmarks pointing at removed docs."],"exampleFix":"// before\nconst bin = await docReader.getDoc(ws, docId)\nreturn bin\n// after\nconst bin = await docReader.getDoc(ws, docId)\nif (!bin) throw new NotFound('doc not found in this workspace')\nreturn bin","handlingStrategy":"validation","validationCode":"const exists = await models.doc.exists(workspaceId, docId)\nif (!exists) return notFoundPage(docId)","typeGuard":null,"tryCatchPattern":"try { await loadDoc(ws, docId) } catch (e) {\n  if (e.code === 'doc_not_found') showEmptyState()\n  else throw e\n}","preventionTips":["Validate docId format and workspace membership before fetch.","Handle 404 with an empty state rather than crashing.","Keep doc lifecycle events in sync with client caches."],"tags":["docs","not-found","storage","replication"],"backgroundTag":null,"analyzedSha":"26c515e050211269e911f7d9cfe162a26c83ed98","analyzedAt":"2026-08-12T13:15:16.447Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}