microsoft/garnet · error · TsavoriteException

Iterator address is less than log HeadAddress in…

Error message

Iterator address is less than log HeadAddress in memory-scan mode

What it means

SpanByteScanIterator.LoadPageIfNeeded throws when frameSize == 0 (no read-back frame / no buffered disk pages), currentAddress < hlogBase.HeadAddress (the address has been evicted or overwritten in memory), and assumeInMemory is false. Pure memory-scan mode cannot serve records that have already rolled off the in-memory log.

Solutions

  1. Increase the log MemorySize so the entire scanned address range stays resident in memory.
  2. Start the scan at or after the current HeadAddress (use hlog.HeadAddress / BeginAddress), not a saved stale address.
  3. Use a disk-backed scan (ensure the read frame is allocated so frameSize > 0) so evicted pages are read back.
  4. If skipping evicted records is acceptable, document it and start the iterator at HeadAddress.

Example fix

// before
using var it = store.Log.Scan(startAddress: staleAddr, ...);

// after: clamp the start address to the in-memory head
long begin = Math.Max(staleAddr, store.Log.HeadAddress);
using var it = store.Log.Scan(startAddress: begin, ...);
Defensive patterns

Strategy: validation

Validate before calling

long begin = Math.Max(requestedStartAddress, store.Log.HeadAddress);
if (!assumeInMemory && requestedStartAddress < store.Log.HeadAddress && frameSize == 0)
    throw new InvalidOperationException($"start {requestedStartAddress} < HeadAddress {store.Log.HeadAddress}; raise MemorySize or use a disk-backed scan");
using var it = store.Log.Scan(begin, ...);

Prevention

When it happens

Trigger: Starting or continuing a memory-only scan (no disk read frame) at an address behind HeadAddress — the log has wrapped and overwritten/evicted those records — without setting assumeInMemory and without a buffered frame to read them back from disk.

Common situations: Long-running scan that falls behind while the log rolls over; resuming a scan from a saved stale address; MemorySize configured too small for the scan range; using the in-memory scan API for data that has spilled to disk.

Related errors


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

Appendix: source

Thrown at libs/storage/Tsavorite/cs/src/core/Allocator/SpanByteScanIterator.cs:142

                return false;
            }

            // Success; caller will suspend the epoch as needed.
            return true;
        }

        private bool LoadPageIfNeeded(out long headAddress, out long currentPage, long stopAddress)
        {
            headAddress = hlogBase.HeadAddress;

            if (currentAddress < hlogBase.BeginAddress && !assumeInMemory)
                currentAddress = hlogBase.BeginAddress;

            // If currentAddress < headAddress and we're not buffering and not guaranteeing the records are in memory, fail.
            if (frameSize == 0 && currentAddress < headAddress && !assumeInMemory)
            {
                // Caller will suspend the epoch.
                throw new TsavoriteException("Iterator address is less than log HeadAddress in memory-scan mode");
            }

            currentPage = hlogBase.GetPage(currentAddress);
            if (currentAddress < headAddress && !assumeInMemory)
                _ = BufferAndLoad(currentAddress, currentPage, currentPage % frameSize, headAddress, stopAddress);

            // Success; keep the epoch held for GetNext (SnapCursorToLogicalAddress will Suspend()).
            return true;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        internal long SnapToLogicalAddressBoundary(ref long logicalAddress, long headAddress, long currentPage)
        {
            var offset = hlogBase.GetOffsetOnPage(logicalAddress);

            // Subtracting offset means this physicalAddress is at the start of the page. Adjust for PageHeader.
            long totalSizes = PageHeader.Size;
            if (currentPage == 0)

View on GitHub (pinned to 951b0fc683)