microsoft/garnet · critical · TsavoriteException

Unable to set first valid segment to {firstValidSegment}, fi

Error message

Unable to set first valid segment to {firstValidSegment}, first available segment on disk is {firstAvailSegment}

What it means

Thrown by VerifyRecoveryInfo during recovery when the checkpoint's beginAddress maps to a segment (firstValidSegment) that is earlier than the first segment still present on the log device (device.StartSegment). In other words, recovery needs a disk segment that has already been deleted/truncated. The store cannot replay records that no longer exist on disk.

Source

Thrown at libs/storage/Tsavorite/cs/src/core/Allocator/AllocatorBase.cs:729

            var firstAvailSegment = device.StartSegment;
            var lastAvailSegment = device.EndSegment;

            if (FlushedUntilAddress > GetFirstValidLogicalAddressOnPage(0))
            {
                var flushedUntilAddress = FlushedUntilAddress;
                int currTailSegment = (int)GetSegment(flushedUntilAddress);
                if (GetOffsetOnSegment(flushedUntilAddress) == 0)
                    currTailSegment--;

                if (currTailSegment > lastAvailSegment)
                    lastAvailSegment = currTailSegment;
            }

            logger?.LogInformation("Available segment range on device: [{firstAvailSegment}--{lastAvailSegment}]", firstAvailSegment, lastAvailSegment);

            if (firstValidSegment < firstAvailSegment)
                throw new TsavoriteException($"Unable to set first valid segment to {firstValidSegment}, first available segment on disk is {firstAvailSegment}");

            if (lastAvailSegment >= 0 && lastValidSegment > lastAvailSegment)
                throw new TsavoriteException($"Unable to set last valid segment to {lastValidSegment}, last available segment on disk is {lastAvailSegment}");

            if (trimLog)
            {
                logger?.LogInformation("Trimming disk segments until (not including) {firstSegment}", firstValidSegment);
                var toAddress = GetStartLogicalAddressOfSegment(firstValidSegment);
                TruncateUntilAddressBlocking(toAddress);
                if (storeFunctions.CallOnTruncate)
                    storeFunctions.OnTruncate(toAddress);

                for (int s = lastValidSegment + 1; s <= lastAvailSegment; s++)
                {
                    logger?.LogInformation("Trimming tail segment {s} on disk", s);
                    RemoveSegment(s);
                }
            }

View on GitHub (pinned to 951b0fc683)

Solutions

  1. Recover from a newer checkpoint whose beginAddress >= device.StartSegment (check logger line 'Available segment range on device').
  2. Restore the full log directory (all segments, not just the checkpoint) from backup so StartSegment <= firstValidSegment.
  3. Delete the stale checkpoint and recover from a later one, or start fresh if the data is disposable.
  4. Ensure the log device path and the checkpoint directory always move together and are never partially pruned.

Example fix

// before: recover from an old checkpoint whose beginAddress predates surviving segments
store.Recover();
// after: pick the latest compatible checkpoint, or restore missing segments
// restore log dir from backup first, then
store.Recover();
Defensive patterns

Strategy: validation

Validate before calling

// Before Recover: ensure the checkpoint's begin address is within surviving segments
var firstNeededSeg = (int)(recoveredCheckpoint.beginAddress >> logSettings.SegmentSizeBits);
if (firstNeededSeg < logDevice.StartSegment)
    throw new InvalidOperationException("Checkpoint begin address predates surviving log segments; restore backup or use a later checkpoint");

Try / catch

try { store.Recover(); }
catch (TsavoriteException ex) when (ex.Message.Contains("first available segment on disk"))
{
    // checkpoint/device mismatch: restore full log dir or pick a later checkpoint
    logger.LogError(ex, "Recovery segment mismatch; checkpoint is older than surviving data");
    throw;
}

Prevention

When it happens

Trigger: Recovering from a HybridLogCheckpoint whose info.beginAddress points below device.StartSegment. Happens when the log device was trimmed (TruncateUntilAddress / RemoveSegment) or a separate process deleted on-disk segments after the checkpoint was taken, or when checkpoint and device directories are out of sync.

Common situations: Recovery attempted against a partially deleted/truncated log directory; checkpoint file copied without its corresponding data segments; manual cleanup of segment files; a prior recovery or store open trimmed the head past this checkpoint's begin address; mismatched checkpoint/log paths after a directory move.

Related errors


AI-assisted analysis of microsoft/garnet@951b0fc683 (2026-08-13). Data as JSON: /api/errors/b23b0cd021e2f9b9. Report an issue: GitHub.