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

  1. Check File.Exists(recoveryPath) before attempting recovery.
  2. Validate the snapshot file size is non-zero and matches expectations before calling.
  3. Keep the previous snapshot until the new one is fully written and verified, to allow fallback.
  4. On version upgrades, regenerate snapshots with the new library version before relying on recovery.
  5. 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

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


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