{"record":{"id":"165e3f218bd15235","repo":"toeverything/AFFiNE","slug":"doc-history-not-found-165e3f","errorCode":"doc_history_not_found","errorMessage":"History of ${docId} at ${timestamp} under Space ${spaceId}.","messagePattern":"History of (.+?) at (.+?) under Space (.+?)\\.","errorType":"exception","errorClass":"DocHistoryNotFound","httpStatus":404,"severity":"error","filePath":"packages/backend/server/src/core/workspaces/controller.ts","lineNumber":366,"sourceCode":"      ts = new Date(timestamp);\n    } catch {\n      throw new InvalidHistoryTimestamp({ timestamp });\n    }\n\n    await this.ac.user(user.id).doc(ws, guid).assert('Doc.Read');\n\n    const history = await this.workspace.getDocHistory(\n      docId.workspace,\n      docId.guid,\n      ts.getTime()\n    );\n\n    if (history) {\n      res.setHeader('content-type', 'application/octet-stream');\n      res.setHeader('cache-control', 'private, max-age=2592000, immutable');\n      res.send(history.bin);\n    } else {\n      throw new DocHistoryNotFound({\n        spaceId: docId.workspace,\n        docId: guid,\n        timestamp: ts.getTime(),\n      });\n    }\n  }\n\n  @Get('/:id/docs/:docId/comment-attachments/:key')\n  @CallMetric('controllers', 'workspace_get_comment_attachment')\n  async commentAttachment(\n    @CurrentUser() user: CurrentUser,\n    @Param('id') workspaceId: string,\n    @Param('docId') docId: string,\n    @Param('key') key: string,\n    @Res() res: Response\n  ) {\n    await this.ac.user(user.id).doc(workspaceId, docId).assert('Doc.Read');\n","sourceCodeStart":348,"sourceCodeEnd":384,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/26c515e050211269e911f7d9cfe162a26c83ed98/packages/backend/server/src/core/workspaces/controller.ts#L348-L384","documentation":"Thrown when getDocHistory returns no history snapshot for the given doc at the given timestamp. The doc exists but no historical version matching that time is available.","triggerScenarios":"Requesting doc history at a timestamp older than retained history, newer than now, in a snapshot gap, or when history snapshots are disabled/not collected for the workspace.","commonSituations":"History retention window expired; snapshot job failed/disabled; timestamp in the future or before doc creation; time-travel on a brand-new doc.","solutions":["Use a timestamp within the retained history window.","Confirm history/snapshot jobs are enabled and running.","Check history storage retention settings.","Fall back to the current doc version if no history exists."],"exampleFix":"// before\nconst h = await workspace.getDocHistory(ws, guid, ts.getTime())\nres.send(h.bin)\n// after\nconst h = await workspace.getDocHistory(ws, guid, ts.getTime())\nif (!h) throw new NotFound('no history at this time')","handlingStrategy":"try-catch","validationCode":"const historyList = await workspace.listDocHistory(ws, guid)\nif (!historyList.some(h => Math.abs(h.timestamp - ts) < SNAP_GAP)) return noHistoryState()","typeGuard":null,"tryCatchPattern":"try { await restoreHistory(guid, ts) } catch (e) {\n  if (e.code === 'doc_history_not_found') showNoHistory()\n  else throw e\n}","preventionTips":["Offer selectable timestamps from the available history list.","Monitor snapshot jobs.","Document retention windows to users."],"tags":["history","not-found","snapshots","docs"],"backgroundTag":null,"analyzedSha":"26c515e050211269e911f7d9cfe162a26c83ed98","analyzedAt":"2026-08-12T13:15:16.447Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}