{"record":{"id":"e41f2cf5d51cfe1a","repo":"microsoft/garnet","slug":"snapshot-path-is-required","errorCode":null,"errorMessage":"Snapshot path is required.","messagePattern":"Snapshot path is required\\.","errorType":"validation","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"libs/native/bftree-garnet/BfTreeService.cs","lineNumber":530,"sourceCode":"        /// TreeHandle but not the managed <see cref=\"BfTreeService\"/> instance.\n        ///\n        /// <para><b>Caller contract:</b> this method does NOT self-serialize. bftree's internal\n        /// <c>snapshot_in_progress</c> flag makes a <c>cpr_snapshot</c> that races another\n        /// snapshot on the same tree <b>silently no-op</b> (no file written) while this method\n        /// still returns success. Callers MUST hold external per-tree serialization (e.g.\n        /// RangeIndex's per-tree snapshot claim) around this call so concurrent snapshots of the\n        /// same handle cannot race.</para>\n        /// </summary>\n        /// <param name=\"handle\">Native BfTree pointer.</param>\n        /// <param name=\"snapshotPath\">Destination path for the snapshot file. The snapshot\n        /// destination is supplied at call time; the caller supplies the path it\n        /// configured for this tree.</param>\n        public static void CprSnapshotByPtr(nint handle, string snapshotPath)\n        {\n            if (handle == nint.Zero)\n                throw new ArgumentException(\"Native handle is null.\", nameof(handle));\n            if (string.IsNullOrEmpty(snapshotPath))\n                throw new ArgumentException(\"Snapshot path is required.\", nameof(snapshotPath));\n\n            var snapBytes = Encoding.UTF8.GetBytes(snapshotPath);\n            int result;\n            fixed (byte* sp = snapBytes)\n            {\n                result = NativeBfTreeMethods.bftree_cpr_snapshot(handle, sp, snapBytes.Length);\n            }\n            if (result != 0)\n                throw new InvalidOperationException(\"Failed to take CPR snapshot of BfTree.\");\n        }\n\n        /// <summary>\n        /// Recover a BfTree from a CPR snapshot file. Unified API for disk-backed and\n        /// memory-backed (cache_only) trees — the storage backend is recorded in the\n        /// snapshot and inferred by the native library.\n        /// </summary>\n        /// <param name=\"recoveryPath\">Source CPR snapshot file path.</param>\n        /// <param name=\"enableSnapshots\">Enable CPR snapshot support on the recovered tree.","sourceCodeStart":512,"sourceCodeEnd":548,"githubUrl":"https://github.com/microsoft/garnet/blob/951b0fc6838721f89d102c2bbe1b914e8d39d700/libs/native/bftree-garnet/BfTreeService.cs#L512-L548","documentation":"Thrown by CprSnapshotByPtr when snapshotPath is null or empty. The native bftree_cpr_snapshot requires a non-empty UTF-8 destination path; an empty one would yield a native failure (it returns -1 on invalid/empty path), so the managed layer rejects it early with a clear ArgumentException instead.","triggerScenarios":"Calling CprSnapshotByPtr(handle, null) or CprSnapshotByPtr(handle, \"\"), e.g. when the per-tree snapshot destination path was never configured or was read from a missing config key.","commonSituations":"A new tree created without a configured snapshot directory; a config migration that left the snapshot path blank; whitespace-only paths are NOT caught here (only null/empty), so a path of spaces would pass this guard but fail natively.","solutions":["Ensure every tree that will be snapshotted has a non-empty snapshot path configured at creation.","Validate snapshotPath with !string.IsNullOrWhiteSpace(path) before calling.","Resolve the path from a central config and fail fast at startup if it is unset.","If the path is optional for this tree, skip the snapshot call rather than pass an empty string."],"exampleFix":"// before\nBfTreeService.CprSnapshotByPtr(handle, configuredPath);\n\n// after\nif (!string.IsNullOrWhiteSpace(configuredPath))\n    BfTreeService.CprSnapshotByPtr(handle, configuredPath);","handlingStrategy":"validation","validationCode":"if (string.IsNullOrWhiteSpace(snapshotPath))\n    throw new ArgumentException(\"Snapshot path is required.\", nameof(snapshotPath));","typeGuard":"static bool IsValidSnapshotPath(string path) => !string.IsNullOrWhiteSpace(path) && Path.IsPathRooted(path);","tryCatchPattern":"try { BfTreeService.CprSnapshotByPtr(handle, snapshotPath); }\ncatch (ArgumentException ex) when (ex.ParamName == nameof(snapshotPath)) { /* log config error */ }","preventionTips":["Configure a per-tree snapshot directory at creation.","Use absolute paths.","Resolve the path from a single config source and validate at startup."],"tags":["bftree","native-interop","snapshot","invalid-argument","configuration"],"backgroundTag":null,"analyzedSha":"951b0fc6838721f89d102c2bbe1b914e8d39d700","analyzedAt":"2026-08-13T19:01:32.939Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}