{"record":{"id":"66f81c32574d233d","repo":"microsoft/garnet","slug":"checkpoint-history-unavailable-need-full-checkpoi","errorCode":null,"errorMessage":"Checkpoint history unavailable, need full checkpoint for {entry}","messagePattern":"Checkpoint history unavailable, need full checkpoint for (.+?)","errorType":"exception","errorClass":"GarnetException","httpStatus":null,"severity":"error","filePath":"libs/cluster/Server/Replication/CheckpointStore.cs","lineNumber":122,"sourceCode":"                        ckptManager.DeleteIndexCheckpoint(toDeleteIndexToken);\n                    }\n                }\n            }\n        }\n\n        /// <summary>\n        /// Add new checkpoint entry to in memory list.\n        /// Assume that addition of new entries executes at completion of a checkpoint.\n        /// Since there can be not concurrent checkpoints this method is not thread safe.\n        /// </summary>\n        /// <param name=\"entry\"></param>\n        /// <param name=\"fullCheckpoint\"></param>\n        public void AddCheckpointEntry(CheckpointEntry entry, bool fullCheckpoint = false)\n        {\n            // If not full checkpoint index checkpoint will be the one of the previous checkpoint\n            if (!fullCheckpoint)\n            {\n                var lastEntry = tail ?? throw new GarnetException($\"Checkpoint history unavailable, need full checkpoint for {entry}\");\n                entry.metadata.storeIndexToken = lastEntry.metadata.storeIndexToken;\n            }\n\n            _ = ValidateCheckpointEntry(entry);\n\n            if (tail == null)\n                head = tail = entry;\n            else\n            {\n                // We don't have multiple writers because this method is called under the CheckpointLock\n                // So it is safe to update in-place.\n                tail.next = entry;\n                tail = tail.next;\n            }\n\n            logger?.LogCheckpointEntry(LogLevel.Trace, nameof(AddCheckpointEntry), entry);\n\n            if (safelyRemoveOutdated)","sourceCodeStart":104,"sourceCodeEnd":140,"githubUrl":"https://github.com/microsoft/garnet/blob/951b0fc6838721f89d102c2bbe1b914e8d39d700/libs/cluster/Server/Replication/CheckpointStore.cs#L104-L140","documentation":"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.","triggerScenarios":"Calling AddCheckpointEntry(entry, fullCheckpoint: false) before any full checkpoint has ever been recorded, so tail is null.","commonSituations":"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.","solutions":["Ensure the first checkpoint after store creation (or after history reset) is taken as a full checkpoint (fullCheckpoint: true).","Audit the checkpoint-scheduling logic so an empty history always forces a full checkpoint.","If the condition is recoverable, automatically promote to a full checkpoint instead of throwing."],"exampleFix":"// before\nstore.AddCheckpointEntry(entry, fullCheckpoint: false);\n// after\nvar isFull = store.IsEmpty || requestedFull;\nstore.AddCheckpointEntry(entry, fullCheckpoint: isFull);","handlingStrategy":"validation","validationCode":"// Force a full checkpoint when history is empty\nbool isFull = requestedFull || storeHistoryIsEmpty;\nif (!isFull && storeHistoryIsEmpty)\n    throw new InvalidOperationException(\"First checkpoint must be a full checkpoint\");\nstore.AddCheckpointEntry(entry, fullCheckpoint: isFull);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Guarantee the first checkpoint after store creation or history reset is full.","Have the checkpoint scheduler force full when the history list is empty.","Consider auto-promoting to full instead of throwing for recoverable cases."],"tags":["cluster","replication","checkpoint","state-machine","garnet"],"backgroundTag":null,"analyzedSha":"951b0fc6838721f89d102c2bbe1b914e8d39d700","analyzedAt":"2026-08-13T19:01:32.939Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}