microsoft/garnet · critical · TsavoriteException

Unable to set last valid segment to {lastValidSegment}, last

Error message

Unable to set last valid segment to {lastValidSegment}, last available segment on disk is {lastAvailSegment}

What it means

Thrown by VerifyRecoveryInfo during recovery when the checkpoint's flushed address maps to a segment (lastValidSegment) that is beyond the last segment present on the log device (lastAvailSegment). Recovery needs the tail segment of the log to reach the checkpoint's flushed/commit point, but that segment is missing on disk.

Source

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

            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);
                }
            }
        }

        /// <summary>Allocate a pinned byte[] for the page at <paramref name="index"/></summary>

View on GitHub (pinned to 951b0fc683)

Solutions

  1. Restore the missing tail segments from backup so lastAvailSegment >= lastValidSegment.
  2. Recover from an earlier checkpoint whose flushed address falls within the surviving segment range.
  3. Verify the log directory is complete (no missing segment files numbered <= lastValidSegment) before calling Recover.
  4. If segments are genuinely lost and no earlier checkpoint is available, start the store fresh and accept data loss.

Example fix

// before
store.Recover(); // lastValidSegment > lastAvailSegment
// after: recover from an earlier checkpoint within the available segment range
checkpointManager.SetRecoveredHLCInfo(earlierCheckpoint);
store.Recover();
Defensive patterns

Strategy: validation

Validate before calling

var lastNeededSeg = (int)(recoveredCheckpoint.flushedLogicalAddress >> logSettings.SegmentSizeBits);
if (lastNeededSeg > logDevice.EndSegment)
    throw new InvalidOperationException("Checkpoint needs tail segments missing from the log device");

Try / catch

try { store.Recover(); }
catch (TsavoriteException ex) when (ex.Message.Contains("last available segment on disk"))
{
    logger.LogError(ex, "Recovery needs tail segments not present; restore backup or use earlier checkpoint");
    throw;
}

Prevention

When it happens

Trigger: Recovering from a checkpoint whose finalLogicalAddress / flushedLogicalAddress falls in a segment greater than device.EndSegment (and the in-memory FlushedUntilAddress doesn't extend it). Occurs when tail segments were lost, the log directory is incomplete, or checkpoint metadata points further than the data present.

Common situations: Incomplete copy of the log directory (missing the newest segment files); checkpoint taken then tail segments deleted; disk corruption/loss of the most recent segments; a checkpoint that is ahead of the actually-flushed data (crash between checkpoint commit and segment flush).

Related errors


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