{"record":{"id":"eb2721d4a10cdd3d","repo":"microsoft/garnet","slug":"bftree-scan-with-end-key-returned-a-null-handle","errorCode":null,"errorMessage":"bftree_scan_with_end_key returned a null handle.","messagePattern":"bftree_scan_with_end_key returned a null handle\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"libs/native/bftree-garnet/BfTreeService.cs","lineNumber":334,"sourceCode":"                NativeBfTreeMethods.bftree_scan_drop(handle);\n            }\n        }\n\n        /// <summary>\n        /// Scan with end key via native pointer using a zero-allocation callback.\n        /// </summary>\n        /// <returns>Number of records passed to the callback.</returns>\n        public static int ScanWithEndKeyByPtrCallback(nint treePtr, ReadOnlySpan<byte> startKey, ReadOnlySpan<byte> endKey, ScanReturnField returnField, ScanRecordAction onRecord)\n        {\n            nint handle;\n            fixed (byte* skp = startKey, ekp = endKey)\n            {\n                handle = NativeBfTreeMethods.bftree_scan_with_end_key(\n                    treePtr, skp, startKey.Length, ekp, endKey.Length, (byte)returnField);\n            }\n\n            if (handle == nint.Zero)\n                throw new InvalidOperationException(\"bftree_scan_with_end_key returned a null handle.\");\n\n            try\n            {\n                Span<byte> buffer = stackalloc byte[8192];\n                return DrainScanIteratorWithCallback(handle, buffer, returnField, onRecord);\n            }\n            finally\n            {\n                NativeBfTreeMethods.bftree_scan_drop(handle);\n            }\n        }\n\n        // ---------------------------------------------------------------\n        // Point operations — span-based (safe wrappers: fixed → PinnedSpanByte → native)\n        // ---------------------------------------------------------------\n\n        /// <summary>\n        /// Insert a key-value pair into the BfTree.","sourceCodeStart":316,"sourceCodeEnd":352,"githubUrl":"https://github.com/microsoft/garnet/blob/951b0fc6838721f89d102c2bbe1b914e8d39d700/libs/native/bftree-garnet/BfTreeService.cs#L316-L352","documentation":"Thrown by BfTreeService.ScanWithEndKeyByPtrCallback when the native bftree_scan_with_end_key function returns a null handle. Similar to the count-based scan, a null handle means the native layer could not start the range scan. This variant takes both a startKey and endKey to define the scan range; invalid or inverted key ranges may also contribute to failure.","triggerScenarios":"Calling ScanWithEndKeyByPtrCallback with an invalid/freed treePtr, or with startKey/endKey values the native layer rejects. The check is at BfTreeService.cs:333. Both keys are pinned and passed to native code.","commonSituations":"Using a tree pointer from a disposed BfTreeService; passing an endKey that sorts before the startKey (inverted range) causing native rejection; corrupted tree state; native memory issues.","solutions":["Verify the treePtr is from a live (non-disposed) BfTreeService.","Ensure the startKey and endKey define a valid, non-inverted range for the tree's key ordering.","Check native logs for the specific scan initialization failure.","Avoid concurrent scan/dispose races by synchronizing access to the tree."],"exampleFix":"// before: inverted key range\nBfTreeService.ScanWithEndKeyByPtrCallback(\n    treePtr,\n    startKey: Encoding.UTF8.GetBytes(\"zzz\"),  // after endKey\n    endKey: Encoding.UTF8.GetBytes(\"aaa\"),     // before startKey\n    returnField, onRecord);\n\n// after: correct order\nBfTreeService.ScanWithEndKeyByPtrCallback(\n    treePtr,\n    startKey: Encoding.UTF8.GetBytes(\"aaa\"),\n    endKey: Encoding.UTF8.GetBytes(\"zzz\"),\n    returnField, onRecord);","handlingStrategy":"validation","validationCode":"if (treePtr == nint.Zero)\n    throw new InvalidOperationException(\"Cannot scan: tree pointer is null/zero.\");\n// Ensure startKey <= endKey lexicographically\nif (startKey.ToArray().SequenceEqual(endKey.ToArray()) == false)\n{\n    var cmp = new ReadOnlySpan<byte>(startKey.ToArray()).SequenceCompareTo(endKey);\n    if (cmp > 0)\n        throw new ArgumentException(\"startKey must be lexicographically <= endKey.\");\n}","typeGuard":"static bool IsValidScanRange(ReadOnlySpan<byte> startKey, ReadOnlySpan<byte> endKey) =>\n    startKey.SequenceCompareTo(endKey) <= 0;","tryCatchPattern":"try\n{\n    BfTreeService.ScanWithEndKeyByPtrCallback(treePtr, startKey, endKey, returnField, onRecord);\n}\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"bftree_scan_with_end_key returned a null handle\"))\n{\n    logger.LogError(ex, \"BfTree range scan failed — check tree pointer validity and key ordering.\");\n    throw;\n}","preventionTips":["Verify startKey sorts before or equal to endKey before scanning.","Ensure the tree pointer comes from a live BfTreeService.","Synchronize scan and dispose operations to prevent use-after-free.","Log native error context when scan handles are null."],"tags":["bftree","native-interop","scan","resource-lifecycle"],"backgroundTag":null,"analyzedSha":"951b0fc6838721f89d102c2bbe1b914e8d39d700","analyzedAt":"2026-08-13T19:01:32.939Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}