{"record":{"id":"1b4dfa6a5e025ad5","repo":"microsoft/FASTER","slug":"recovering-to-a-specific-version-within-a-token-is-only","errorCode":null,"errorMessage":"Recovering to a specific version within a token is only supported for incremental snapshots","messagePattern":"Recovering to a specific version within a token is only supported for incremental snapshots","errorType":"exception","errorClass":"FasterException","httpStatus":null,"severity":"error","filePath":"cs/src/core/Index/Recovery/Recovery.cs","lineNumber":352,"sourceCode":"            if (recoveredICInfo.IsDefault())\n            {\n                logger?.LogInformation(\"No index checkpoint found, recovering from beginning of log\");\n            }\n        }\n\n        private static bool IsCompatible(in IndexRecoveryInfo indexInfo, in HybridLogRecoveryInfo recoveryInfo)\n        {\n            var l1 = indexInfo.finalLogicalAddress;\n            var l2 = recoveryInfo.finalLogicalAddress;\n            return l1 <= l2;\n        }\n\n        private long InternalRecover(Guid indexToken, Guid hybridLogToken, int numPagesToPreload, bool undoNextVersion, long recoverTo)\n        {\n            GetRecoveryInfo(indexToken, hybridLogToken, out HybridLogCheckpointInfo recoveredHLCInfo, out IndexCheckpointInfo recoveredICInfo);\n            if (recoverTo != -1 && recoveredHLCInfo.deltaLog == null)\n            {\n                throw new FasterException(\"Recovering to a specific version within a token is only supported for incremental snapshots\");\n            }\n            return InternalRecover(recoveredICInfo, recoveredHLCInfo, numPagesToPreload, undoNextVersion, recoverTo);\n        }\n\n        private ValueTask<long> InternalRecoverAsync(Guid indexToken, Guid hybridLogToken, int numPagesToPreload, bool undoNextVersion, long recoverTo, CancellationToken cancellationToken)\n        {\n            GetRecoveryInfo(indexToken, hybridLogToken, out HybridLogCheckpointInfo recoveredHLCInfo, out IndexCheckpointInfo recoveredICInfo);\n            return InternalRecoverAsync(recoveredICInfo, recoveredHLCInfo, numPagesToPreload, undoNextVersion, recoverTo, cancellationToken);\n        }\n\n        private void GetRecoveryInfo(Guid indexToken, Guid hybridLogToken, out HybridLogCheckpointInfo recoveredHLCInfo, out IndexCheckpointInfo recoveredICInfo)\n        {\n            logger?.LogInformation(\"********* Primary Recovery Information ********\");\n            logger?.LogInformation(\"Index Checkpoint: {indexToken}\", indexToken);\n            logger?.LogInformation(\"HybridLog Checkpoint: {hybridLogToken}\", hybridLogToken);\n\n\n            // Recovery appropriate context information","sourceCodeStart":334,"sourceCodeEnd":370,"githubUrl":"https://github.com/microsoft/FASTER/blob/321d872eabda6a0345c8bd76419f89723ed864ae/cs/src/core/Index/Recovery/Recovery.cs#L334-L370","documentation":"InternalRecover throws this when a non-default recoverTo (specific version within a token) is requested but the HybridLog checkpoint has no delta log (deltaLog == null), i.e. it is a full snapshot. Version-precise recovery within a token is only possible with incremental snapshots, because only delta logs record the intermediate commit points between full snapshots.","triggerScenarios":"Calling Recover(recoverTo: someVersion) or RecoverAsync with a version argument while passing hybridLogToken taken as a full snapshot (TakeFullCheckpoint or TakeHybridLogCheckpoint with fullSnapshot: true).","commonSituations":"Mixing snapshot modes: team switched checkpoints to full snapshots for speed but application code still requests version-level recovery; recovering with tokens copied from another deployment that used incremental snapshots.","solutions":["Take checkpoints as incremental snapshots: pass fullSnapshot: false to TakeHybridLogCheckpoint (or configure CheckpointSettings) so delta logs are retained.","Recover to the whole token (recoverTo = -1) instead of a specific version when only full snapshots exist.","Recover to the closest available token/version that the existing full snapshot supports."],"exampleFix":"// before\nfaster.Recover(indexToken, hlogToken, recoverTo: 1234); // hlog token is a full snapshot\n// after\nawait faster.TakeHybridLogCheckpointAsync(fullSnapshot: false); // enable delta logs\nfaster.Recover(indexToken, hlogToken, recoverTo: 1234);","handlingStrategy":"validation","validationCode":"// Only request version-level recovery if the token came from an incremental snapshot\nif (recoverTo != -1 && !checkpointWasIncremental(hybridLogToken))\n    throw new InvalidOperationException(\"Version recovery requires an incremental snapshot; recover to whole token instead.\");","typeGuard":null,"tryCatchPattern":"try\n{\n    await faster.RecoverAsync(recoverTo: version);\n}\ncatch (FasterException ex) when (ex.Message.Contains(\"only supported for incremental snapshots\"))\n{\n    await faster.RecoverAsync(); // fall back to recovering the whole token\n}","preventionTips":["Keep snapshot mode consistent between checkpointing and recovery code paths.","Record whether each token is a full or incremental snapshot in your token metadata store.","Use fullSnapshot: false when version-precise recovery is a requirement."],"tags":["faster","recovery","checkpoint","snapshot"],"backgroundTag":"unsupported-operation","analyzedSha":"321d872eabda6a0345c8bd76419f89723ed864ae","analyzedAt":"2026-09-15T22:18:00.693Z","contentChangedAt":"2026-09-15T22:18:00.693Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}