{"record":{"id":"5c2a897c905c9abd","repo":"microsoft/garnet","slug":"failed-to-recover-bftree-from-cpr-snapshot-recov","errorCode":null,"errorMessage":"Failed to recover BfTree from CPR snapshot '{recoveryPath}'.","messagePattern":"Failed to recover BfTree from CPR snapshot '(.+?)'\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"critical","filePath":"libs/native/bftree-garnet/BfTreeService.cs","lineNumber":570,"sourceCode":"            string recoveryPath,\n            bool enableSnapshots,\n            StorageBackendType storageBackend)\n        {\n            if (string.IsNullOrEmpty(recoveryPath))\n                throw new ArgumentException(\"recoveryPath is required.\", nameof(recoveryPath));\n\n            var recoveryBytes = Encoding.UTF8.GetBytes(recoveryPath);\n            byte useSnapshot = (byte)(enableSnapshots ? 1 : 0);\n            nint treePtr;\n            fixed (byte* rp = recoveryBytes)\n            {\n                treePtr = NativeBfTreeMethods.bftree_new_from_cpr_snapshot(\n                    rp, recoveryBytes.Length,\n                    useSnapshot,\n                    null, 0);\n            }\n            if (treePtr == 0)\n                throw new InvalidOperationException($\"Failed to recover BfTree from CPR snapshot '{recoveryPath}'.\");\n            return new BfTreeService(treePtr, storageBackend, filePath: null);\n        }\n\n        /// <summary>\n        /// Drains scan iterator via callback — zero per-record allocation.\n        /// </summary>\n        private static int DrainScanIteratorWithCallback(\n            nint handle, Span<byte> buffer, ScanReturnField returnField, ScanRecordAction onRecord)\n        {\n            int count = 0;\n            while (true)\n            {\n                int keyLen = 0, valueLen = 0;\n                int hasNext;\n                fixed (byte* bp = buffer)\n                    hasNext = NativeBfTreeMethods.bftree_scan_next(\n                        handle, bp, buffer.Length, &keyLen, &valueLen);\n                if (hasNext == 0)","sourceCodeStart":552,"sourceCodeEnd":588,"githubUrl":"https://github.com/microsoft/garnet/blob/951b0fc6838721f89d102c2bbe1b914e8d39d700/libs/native/bftree-garnet/BfTreeService.cs#L552-L588","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nvar tree = BfTreeService.RecoverFromCprSnapshot(recoveryPath, true, backend);\n\n// after\nBfTreeService tree;\nif (File.Exists(recoveryPath) && new FileInfo(recoveryPath).Length > 0) {\n    try { tree = BfTreeService.RecoverFromCprSnapshot(recoveryPath, true, backend); }\n    catch (InvalidOperationException) { tree = CreateFreshTree(); }\n} else {\n    tree = CreateFreshTree();\n}","handlingStrategy":"fallback","validationCode":"if (!File.Exists(recoveryPath) || new FileInfo(recoveryPath).Length == 0)\n    throw new InvalidOperationException(\"Snapshot file missing or empty; cannot recover.\");","typeGuard":"static bool IsRecoverable(string path) =>\n    File.Exists(path) && new FileInfo(path).Length > 0;","tryCatchPattern":"try { tree = BfTreeService.RecoverFromCprSnapshot(recoveryPath, true, backend); }\ncatch (InvalidOperationException) { logger.LogError(\"Snapshot corrupt; starting fresh.\"); tree = CreateFreshTree(); }","preventionTips":["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."],"tags":["bftree","native-interop","recovery","data-loss","snapshot-corruption"],"backgroundTag":null,"analyzedSha":"951b0fc6838721f89d102c2bbe1b914e8d39d700","analyzedAt":"2026-08-13T19:01:32.939Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}