{"record":{"id":"75e3d7990481a4ef","repo":"overleaf/overleaf","slug":"non-persisted-changes-can-t-be-applied-to-base-ver","errorCode":null,"errorMessage":"Non-persisted changes can't be applied to base version","messagePattern":"Non-persisted changes can't be applied to base version","errorType":"exception","errorClass":"VersionOutOfBoundsError","httpStatus":null,"severity":"error","filePath":"services/history-v1/storage/lib/chunk_store/redis.js","lineNumber":487,"sourceCode":"    )\n  } catch (err) {\n    metrics.inc('chunk_store.redis.get_non_persisted_changes', 1, {\n      status: 'error',\n    })\n    throw err\n  }\n\n  const status = result[0]\n  metrics.inc('chunk_store.redis.get_non_persisted_changes', 1, {\n    status,\n  })\n\n  if (status === 'ok') {\n    return result[1].map(json => Change.fromRaw(JSON.parse(json)))\n  } else if (status === 'not_found') {\n    return []\n  } else if (status === 'out_of_bounds') {\n    throw new VersionOutOfBoundsError(\n      \"Non-persisted changes can't be applied to base version\",\n      { projectId, baseVersion }\n    )\n  } else {\n    throw new OError('unknown status for get_non_persisted_changes', {\n      projectId,\n      baseVersion,\n      status,\n    })\n  }\n}\n\nrclient.defineCommand('set_persisted_version', {\n  numberOfKeys: 4,\n  lua: `\n    local headVersionKey = KEYS[1]\n    local persistedVersionKey = KEYS[2]\n    local persistTimeKey = KEYS[3]","sourceCodeStart":469,"sourceCodeEnd":505,"githubUrl":"https://github.com/overleaf/overleaf/blob/28ad3b03b71cb4311decdcb55c36b33ec10d72db/services/history-v1/storage/lib/chunk_store/redis.js#L469-L505","documentation":"getNonPersistedChanges asks the Redis script for changes after a given baseVersion. Status 'out_of_bounds' means the requested baseVersion is no longer within the non-persisted window — the changes starting at that version were already persisted into postgres (or compacted away) — so a VersionOutOfBoundsError is thrown.","triggerScenarios":"Calling getNonPersistedChanges(projectId, baseVersion) where baseVersion is below the oldest retained non-persisted change, typically because a persist job already flushed those changes to postgres.","commonSituations":"A reader computing diff history against a base version that has just been persisted; background persistence racing an interactive read; asking for version 0 on a project whose history start has advanced.","solutions":["Catch VersionOutOfBoundsError and fall back to loading the persisted chunk history from postgres for versions at/below the persisted boundary.","Clamp baseVersion to the store's current non-persisted start version before calling.","Subscribe to/track the persisted version and only call this API for versions > persistedVersion."],"exampleFix":"// before\nconst changes = await chunkStore.getNonPersistedChanges(projectId, baseVersion)\n\n// after\nlet changes\ntry {\n  changes = await chunkStore.getNonPersistedChanges(projectId, baseVersion)\n} catch (err) {\n  if (err instanceof VersionOutOfBoundsError) {\n    changes = await loadPersistedChangesFromPostgres(projectId, baseVersion)\n  } else {\n    throw err\n  }\n}","handlingStrategy":"fallback","validationCode":"const persisted = await chunkStore.getProjectPersistedVersion(projectId); if (baseVersion <= persisted) { usePersistedPath() }","typeGuard":"function isVersionOutOfBounds(err) { return err instanceof VersionOutOfBoundsError }","tryCatchPattern":"try { return await chunkStore.getNonPersistedChanges(projectId, baseVersion) } catch (err) { if (isVersionOutOfBounds(err)) { return await loadFromPostgres(projectId, baseVersion) } throw err }","preventionTips":["Track the persisted version and never request non-persisted changes below it.","Prefer range queries over absolute base versions when walking history.","Remember persistence can happen concurrently; always code the postgres fallback path."],"tags":["redis","chunk-store","versioning","history-v1"],"backgroundTag":"version-out-of-bounds","analyzedSha":"28ad3b03b71cb4311decdcb55c36b33ec10d72db","analyzedAt":"2026-09-03T02:10:22.807Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T07:17:11.731Z"}