toeverything/AFFiNE · error · DocHistoryNotFound
doc_history_not_found
doc_history_not_found
Error message
History of ${docId} at ${timestamp} under Space ${spaceId}. What it means
Thrown by `rollbackDoc` when `getDocHistory(spaceId, docId, timestamp)` returns null — i.e., no history snapshot exists for that exact `timestamp`. The rollback cannot proceed without a target history record, so it aborts with `DocHistoryNotFound` (category `resource_not_found`), interpolating `docId`, `timestamp`, and `spaceId` into the message.
Source
Thrown at packages/backend/server/src/core/doc/adapters/workspace.ts:207
return {
spaceId: workspaceId,
docId,
bin: history.blob,
timestamp: history.timestamp,
editor: history.editor?.id,
};
}
override async rollbackDoc(
spaceId: string,
docId: string,
timestamp: number,
editorId?: string
): Promise<void> {
await using _lock = await this.lockDocForUpdate(spaceId, docId);
const toSnapshot = await this.getDocHistory(spaceId, docId, timestamp);
if (!toSnapshot) {
throw new DocHistoryNotFound({ spaceId, docId, timestamp });
}
const fromSnapshot = await this.getDocSnapshot(spaceId, docId);
if (!fromSnapshot) {
throw new DocNotFound({ spaceId, docId });
}
// force create a new history record after rollback
await this.createDocHistory(
{
...fromSnapshot,
// override the editor to the one who requested the rollback
editor: editorId,
},
true
);
// WARN:View on GitHub (pinned to 26c515e050)
Solutions
- Re-fetch the available history list (`listDocHistories`) and only offer rollback to timestamps present in it.
- Verify the requested `timestamp` exactly matches a stored history row (not a client-approximated value).
- Check the workspace's history retention config (`historyMaxAge`) — expired history cannot be restored.
- Surface `doc_history_not_found` to the user as 'this history version is no longer available'.
Example fix
// before
await adapter.rollbackDoc(spaceId, docId, pickedTimestamp, editorId);
// after
const available = await adapter.listDocHistories(spaceId, docId, { limit: 50 });
if (!available.some(h => h.timestamp === pickedTimestamp)) {
toast('This history version is no longer available.');
return;
}
await adapter.rollbackDoc(spaceId, docId, pickedTimestamp, editorId); Defensive patterns
Strategy: validation
Validate before calling
// Only offer rollback to timestamps present in the stored history
const history = await adapter.listDocHistories(spaceId, docId, { limit: 100 });
const validTimestamps = new Set(history.map(h => h.timestamp));
if (!validTimestamps.has(requestedTimestamp)) {
toast('This history version is no longer available.');
return;
}
await adapter.rollbackDoc(spaceId, docId, requestedTimestamp, editorId); Type guard
function isHistoryEntry(value: unknown): value is { timestamp: number; blob: Buffer } {
return typeof value === 'object' && value !== null &&
typeof (value as { timestamp?: unknown }).timestamp === 'number';
} Try / catch
try {
await adapter.rollbackDoc(spaceId, docId, requestedTimestamp, editorId);
} catch (e) {
if ((e as { code?: string }).code === 'doc_history_not_found') {
toast('This history version is no longer available.');
return;
}
throw e;
} Prevention
- Populate the rollback picker from `listDocHistories`, not from a stale client cache.
- Ensure the requested `timestamp` exactly matches a stored history row.
- Account for `historyMaxAge`/`historyMinInterval` retention when offering versions.
- Refresh the history list right before rolling back.
When it happens
Trigger: Requesting a rollback to a `timestamp` for which no history row exists: the timestamp predates history retention, was pruned by `historyMaxAge`, was never snapshotted (interval not elapsed), or the doc has no history at all.
Common situations: History cleanup/expiry removing older rows; a client sending a stale timestamp from an old history list; workspaces whose `historyMaxAge`/`historyMinInterval` config suppresses frequent snapshots; rollback after the history table was trimmed.
Related errors
- doc_not_found
- Can not find the version to rollback to.
- not_found
- failed_to_save_updates
- failed_to_upsert_snapshot
AI-assisted analysis of toeverything/AFFiNE@26c515e050 (2026-08-12).
Data as JSON: /api/errors/d0ac5fc86b4556e7.
Report an issue: GitHub.