microsoft/garnet · error · GarnetException

Checkpoint history unavailable, need full checkpoint for {en

Error message

Checkpoint history unavailable, need full checkpoint for {entry}

What it means

Thrown by CheckpointStore.AddCheckpointEntry when adding a non-full (incremental) checkpoint but the history list is empty (tail == null). Incremental checkpoints reuse the previous checkpoint's storeIndexToken, so with no prior checkpoint there is nothing to inherit; a full checkpoint is required first to seed the history.

Source

Thrown at libs/cluster/Server/Replication/CheckpointStore.cs:122

                        ckptManager.DeleteIndexCheckpoint(toDeleteIndexToken);
                    }
                }
            }
        }

        /// <summary>
        /// Add new checkpoint entry to in memory list.
        /// Assume that addition of new entries executes at completion of a checkpoint.
        /// Since there can be not concurrent checkpoints this method is not thread safe.
        /// </summary>
        /// <param name="entry"></param>
        /// <param name="fullCheckpoint"></param>
        public void AddCheckpointEntry(CheckpointEntry entry, bool fullCheckpoint = false)
        {
            // If not full checkpoint index checkpoint will be the one of the previous checkpoint
            if (!fullCheckpoint)
            {
                var lastEntry = tail ?? throw new GarnetException($"Checkpoint history unavailable, need full checkpoint for {entry}");
                entry.metadata.storeIndexToken = lastEntry.metadata.storeIndexToken;
            }

            _ = ValidateCheckpointEntry(entry);

            if (tail == null)
                head = tail = entry;
            else
            {
                // We don't have multiple writers because this method is called under the CheckpointLock
                // So it is safe to update in-place.
                tail.next = entry;
                tail = tail.next;
            }

            logger?.LogCheckpointEntry(LogLevel.Trace, nameof(AddCheckpointEntry), entry);

            if (safelyRemoveOutdated)

View on GitHub (pinned to 951b0fc683)

Solutions

  1. Ensure the first checkpoint after store creation (or after history reset) is taken as a full checkpoint (fullCheckpoint: true).
  2. Audit the checkpoint-scheduling logic so an empty history always forces a full checkpoint.
  3. If the condition is recoverable, automatically promote to a full checkpoint instead of throwing.

Example fix

// before
store.AddCheckpointEntry(entry, fullCheckpoint: false);
// after
var isFull = store.IsEmpty || requestedFull;
store.AddCheckpointEntry(entry, fullCheckpoint: isFull);
Defensive patterns

Strategy: validation

Validate before calling

// Force a full checkpoint when history is empty
bool isFull = requestedFull || storeHistoryIsEmpty;
if (!isFull && storeHistoryIsEmpty)
    throw new InvalidOperationException("First checkpoint must be a full checkpoint");
store.AddCheckpointEntry(entry, fullCheckpoint: isFull);

Prevention

When it happens

Trigger: Calling AddCheckpointEntry(entry, fullCheckpoint: false) before any full checkpoint has ever been recorded, so tail is null.

Common situations: First-ever checkpoint taken with the full-checkpoint flag accidentally false; history wiped/recreated and the next checkpoint assumed incremental; a startup race where the store is empty but an incremental is queued.

Related errors


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