{"record":{"id":"56ee084ce1a65aa8","repo":"microsoft/FASTER","slug":"use-getnext-out-recordinfo-to-retrieve-references-to-key","errorCode":null,"errorMessage":"Use GetNext(out RecordInfo) to retrieve references to key/value","messagePattern":"Use GetNext\\(out RecordInfo\\) to retrieve references to key/value","errorType":"exception","errorClass":"NotSupportedException","httpStatus":null,"severity":"error","filePath":"cs/src/core/Allocator/VarLenBlittableScanIterator.cs","lineNumber":233,"sourceCode":"        }\n\n        [MethodImpl(MethodImplOptions.AggressiveInlining)]\n        long GetPhysicalAddress(long currentAddress, long headAddress, long currentPage, long offset)\n        {\n            long physicalAddress;\n            if (currentAddress >= headAddress || forceInMemory)\n                physicalAddress = hlog.GetPhysicalAddress(currentAddress);\n            else\n                physicalAddress = frame.GetPhysicalAddress(currentPage % frameSize, offset);\n            return physicalAddress;\n        }\n\n        /// <summary>\n        /// Get next record in iterator\n        /// </summary>\n        /// <returns></returns>\n        public bool GetNext(out RecordInfo recordInfo, out Key key, out Value value)\n            => throw new NotSupportedException(\"Use GetNext(out RecordInfo) to retrieve references to key/value\");\n\n        /// <summary>\n        /// Dispose iterator\n        /// </summary>\n        public override void Dispose()\n        {\n            base.Dispose();\n            memory?.Return();\n            memory = null;\n            frame?.Dispose();\n        }\n\n        internal override void AsyncReadPagesFromDeviceToFrame<TContext>(long readPageStart, int numPages, long untilAddress, TContext context, out CountdownEvent completed, long devicePageOffset = 0, IDevice device = null, IDevice objectLogDevice = null, CancellationTokenSource cts = null)\n            => hlog.AsyncReadPagesFromDeviceToFrame(readPageStart, numPages, untilAddress, AsyncReadPagesCallback, context, frame, out completed, devicePageOffset, device, objectLogDevice);\n\n        private unsafe void AsyncReadPagesCallback(uint errorCode, uint numBytes, object context)\n        {\n            var result = (PageAsyncReadResult<Empty>)context;","sourceCodeStart":215,"sourceCodeEnd":251,"githubUrl":"https://github.com/microsoft/FASTER/blob/321d872eabda6a0345c8bd76419f89723ed864ae/cs/src/core/Allocator/VarLenBlittableScanIterator.cs#L215-L251","documentation":"VarLenBlittableScanIterator's GetNext overload that returns key and value by value is intentionally not supported: the blittable iterator works by reference into the log's memory pages, which can be invalidated by subsequent reads. Callers must use GetNext(out RecordInfo) and then read the key/value references (e.g. via iterator.GetKey/GetValue or ConvertToRecord) before advancing. The method throws NotSupportedException as a compile-time-visible API deterrent.","triggerScenarios":"Calling the public GetNext(out RecordInfo, out Key, out Value) overload on a VarLenBlittableScanIterator instance — often via code written for the IObjectAllocator scan iterator whose GetNext returns copies of key/value objects.","commonSituations":"Reusing generic scan helper code that was written for ObjectAllocator's by-value iterator; LINQ or interface-driven code calling the 3-out overload through a shared abstraction; migrating from the classic BlittableAllocator ScanIterator to VarLenBlittableScanIterator.","solutions":["Call GetNext(out RecordInfo recordInfo) and access the record via reference APIs (GetKey/GetValue or ref Get* methods) before moving to the next record.","If you need durable copies, materialize key/value with ConvertToRecord or copy to your own structs immediately after GetNext.","Refactor shared scan helpers to call the RecordInfo overload, or special-case the varlen blittable iterator.","Compile-time: avoid the 3-out overload in new code; it exists only to fail fast."],"exampleFix":"// before\nwhile (iter.GetNext(out RecordInfo info, out MyKey key, out MyValue value)) { Process(key, value); }\n// after\nwhile (iter.GetNext(out RecordInfo info)) {\n    ref var key = ref iter.GetKey();\n    ref var value = ref iter.GetValue();\n    Process(key, value);\n}","handlingStrategy":"type-guard","validationCode":"// use the supported overload only\nif (iter is VarLenBlittableScanIterator<K, V>)\n    iter.GetNext(out RecordInfo info); // then ref GetKey()/GetValue()","typeGuard":"static bool IsByRefScanIterator<K, V>(ScanIteratorBase<K, V> iter)\n    => iter is VarLenBlittableScanIterator<K, V>;","tryCatchPattern":null,"preventionTips":["Never call the 3-out GetNext overload on varlen blittable iterators","Copy/convert the record (ConvertToRecord) before advancing the iterator if you need it later","Keep object-iterator and by-ref-iterator code paths separate","Let the compiler see the concrete iterator type instead of routing through shared overloads"],"tags":["scan-iterator","api-usage","blittable","not-supported"],"backgroundTag":"deprecated-api-usage","analyzedSha":"321d872eabda6a0345c8bd76419f89723ed864ae","analyzedAt":"2026-09-15T22:18:00.693Z","contentChangedAt":"2026-09-15T22:18:00.693Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}