microsoft/garnet · critical · InvalidOperationException
Failed to recover BfTree from CPR snapshot '{recoveryPath}'.
Error message
Failed to recover BfTree from CPR snapshot '{recoveryPath}'. What it means
Thrown by RecoverFromCprSnapshot when bftree_new_from_cpr_snapshot returns IntPtr.Zero. The native constructor fails (returns null) when the snapshot file is missing, corrupt, truncated, of an incompatible version, or unreadable. All managed arguments passed validation, so this is a genuine recovery failure surfaced as InvalidOperationException.
Source
Thrown at libs/native/bftree-garnet/BfTreeService.cs:570
string recoveryPath,
bool enableSnapshots,
StorageBackendType storageBackend)
{
if (string.IsNullOrEmpty(recoveryPath))
throw new ArgumentException("recoveryPath is required.", nameof(recoveryPath));
var recoveryBytes = Encoding.UTF8.GetBytes(recoveryPath);
byte useSnapshot = (byte)(enableSnapshots ? 1 : 0);
nint treePtr;
fixed (byte* rp = recoveryBytes)
{
treePtr = NativeBfTreeMethods.bftree_new_from_cpr_snapshot(
rp, recoveryBytes.Length,
useSnapshot,
null, 0);
}
if (treePtr == 0)
throw new InvalidOperationException($"Failed to recover BfTree from CPR snapshot '{recoveryPath}'.");
return new BfTreeService(treePtr, storageBackend, filePath: null);
}
/// <summary>
/// Drains scan iterator via callback — zero per-record allocation.
/// </summary>
private static int DrainScanIteratorWithCallback(
nint handle, Span<byte> buffer, ScanReturnField returnField, ScanRecordAction onRecord)
{
int count = 0;
while (true)
{
int keyLen = 0, valueLen = 0;
int hasNext;
fixed (byte* bp = buffer)
hasNext = NativeBfTreeMethods.bftree_scan_next(
handle, bp, buffer.Length, &keyLen, &valueLen);
if (hasNext == 0)View on GitHub (pinned to 951b0fc683)
Solutions
- Check File.Exists(recoveryPath) before attempting recovery.
- Validate the snapshot file size is non-zero and matches expectations before calling.
- Keep the previous snapshot until the new one is fully written and verified, to allow fallback.
- On version upgrades, regenerate snapshots with the new library version before relying on recovery.
- On failure, fall back to creating a fresh empty tree and re-ingest data, rather than crashing the host.
Example fix
// before
var tree = BfTreeService.RecoverFromCprSnapshot(recoveryPath, true, backend);
// after
BfTreeService tree;
if (File.Exists(recoveryPath) && new FileInfo(recoveryPath).Length > 0) {
try { tree = BfTreeService.RecoverFromCprSnapshot(recoveryPath, true, backend); }
catch (InvalidOperationException) { tree = CreateFreshTree(); }
} else {
tree = CreateFreshTree();
} Defensive patterns
Strategy: fallback
Validate before calling
if (!File.Exists(recoveryPath) || new FileInfo(recoveryPath).Length == 0)
throw new InvalidOperationException("Snapshot file missing or empty; cannot recover."); Type guard
static bool IsRecoverable(string path) =>
File.Exists(path) && new FileInfo(path).Length > 0; Try / catch
try { tree = BfTreeService.RecoverFromCprSnapshot(recoveryPath, true, backend); }
catch (InvalidOperationException) { logger.LogError("Snapshot corrupt; starting fresh."); tree = CreateFreshTree(); } Prevention
- Verify snapshot existence and non-zero size before recovery.
- Write snapshots atomically (temp file + rename) to avoid truncated files.
- Keep one prior good snapshot for fallback.
- Regenerate snapshots after bftree library version upgrades.
When it happens
Trigger: The snapshot file at recoveryPath does not exist (managed only checked non-empty, not existence), is partially written (a crashed previous snapshot), is from an incompatible bftree version, or cannot be read due to permissions/IO error.
Common situations: Restart after a crash mid-snapshot leaving a truncated file; upgrading the bftree native library to a version with an incompatible snapshot format; a snapshot file deleted by a cleanup job; permissions changed between snapshot write and recovery read.
Related errors
- recoveryPath is required.
- filePath is required for disk-backed trees.
- Failed to create BfTree instance.
- bftree_scan_with_count returned a null handle.
- bftree_scan_with_end_key returned a null handle.
AI-assisted analysis of microsoft/garnet@951b0fc683 (2026-08-13).
Data as JSON: /api/errors/5c2a897c905c9abd.
Report an issue: GitHub.