{"record":{"id":"0d4ec78e4746736f","repo":"microsoft/FASTER","slug":"unable-to-set-first-valid-segment-to-firstvalidsegment-first","errorCode":null,"errorMessage":"Unable to set first valid segment to {firstValidSegment}, first available segment on disk is {firstAvailSegment}","messagePattern":"Unable to set first valid segment to (.+?), first available segment on disk is (.+?)","errorType":"exception","errorClass":"FasterException","httpStatus":null,"severity":"error","filePath":"cs/src/core/Allocator/AllocatorBase.cs","lineNumber":1117,"sourceCode":"            logger?.LogInformation(\"Recovery requires disk segments in range [{firstSegment}--{tailStartSegment}]\", firstValidSegment, lastValidSegment);\n\n            var firstAvailSegment = device.StartSegment;\n            var lastAvailSegment = device.EndSegment;\n\n            if (FlushedUntilAddress > GetFirstValidLogicalAddress(0))\n            {\n                int currTailSegment = (int)(FlushedUntilAddress >> LogSegmentSizeBits);\n                if ((FlushedUntilAddress & ((1L << LogSegmentSizeBits) - 1)) == 0)\n                    currTailSegment--;\n\n                if (currTailSegment > lastAvailSegment)\n                    lastAvailSegment = currTailSegment;\n            }\n\n            logger?.LogInformation(\"Available segment range on device: [{firstAvailSegment}--{lastAvailSegment}]\", firstAvailSegment, lastAvailSegment);\n\n            if (firstValidSegment < firstAvailSegment)\n                throw new FasterException($\"Unable to set first valid segment to {firstValidSegment}, first available segment on disk is {firstAvailSegment}\");\n\n            if (lastAvailSegment >= 0 && lastValidSegment > lastAvailSegment)\n                throw new FasterException($\"Unable to set last valid segment to {lastValidSegment}, last available segment on disk is {lastAvailSegment}\");\n\n            if (trimLog)\n            {\n                logger?.LogInformation(\"Trimming disk segments until (not including) {firstSegment}\", firstValidSegment);\n                TruncateUntilAddressBlocking(firstValidSegment << LogSegmentSizeBits);\n\n                for (int s = lastValidSegment + 1; s <= lastAvailSegment; s++)\n                {\n                    logger?.LogInformation(\"Trimming tail segment {s} on disk\", s);\n                    RemoveSegment(s);\n                }\n            }\n        }\n\n        /// <summary>","sourceCodeStart":1099,"sourceCodeEnd":1135,"githubUrl":"https://github.com/microsoft/FASTER/blob/321d872eabda6a0345c8bd76419f89723ed864ae/cs/src/core/Allocator/AllocatorBase.cs#L1099-L1135","documentation":"During recovery from a checkpoint, FASTER scans the log device to find the range of segments actually present on disk. The checkpoint records the first valid segment of the log; if the requested firstValidSegment is earlier than the first segment available on disk, recovery cannot restore that log state (data was truncated or the wrong device is attached), so it throws. This prevents silently recovering a log with a hole at the front.","triggerScenarios":"Calling Recover() with a checkpoint whose first valid segment precedes the oldest segment on the attached LogDevice - typically after the log was truncated, a different (empty/new) device was supplied, or checkpoint metadata references a device that no longer holds those segments.","commonSituations":"Pointing recovery at a fresh/empty log device while loading an old checkpoint; manual deletion or trimming of log files; moving checkpoints between machines with different disk contents.","solutions":["Attach the correct original LogDevice that contains the checkpoint's segments (verify the path/contents).","Recover from a checkpoint whose firstValidSegment exists on the current device (e.g. use the latest valid checkpoint index).","If the log was intentionally truncated, re-open the store without the stale checkpoint instead of recovering it."],"exampleFix":"// before\nusing var store = new FasterKV<Key, Value>(size, settings); // fresh device path\nstore.Recover(new CheckpointSettings { CheckpointDir = oldDir }); // checkpoint expects old segments\n\n// after\n// attach the same device/checkpoint set the checkpoint was taken with, or:\n// start fresh without calling Recover on a checkpoint whose log segments are gone.","handlingStrategy":"try-catch","validationCode":"// verify the oldest segment exists before recovering\nbool deviceHasSegment(IDevice dev, long segment) =>\n    dev.GetFileSize(segment << segmentSizeBits) > 0 || segment == 0;","typeGuard":null,"tryCatchPattern":"try { store.Recover(checkpointSettings); }\ncatch (FasterException ex) when (ex.Message.Contains(\"first valid segment\")) {\n    // fall back to an older checkpoint or start fresh\n}","preventionTips":["Keep the log device and checkpoint directories together; never restore one without the other.","Do not manually delete or trim early log segments while checkpoints reference them.","Test disaster-restore procedures so partial restores are caught before production recovery."],"tags":["csharp","recovery","faster","checkpoint"],"backgroundTag":"resource-not-found","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"}