microsoft/garnet · error · TsavoriteException
Iterator address is less than log HeadAddress in memory-scan
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.
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)
Solutions
- Increase the log MemorySize so the entire scanned address range stays resident in memory.
- Start the scan at or after the current HeadAddress (use hlog.HeadAddress / BeginAddress), not a saved stale address.
- Use a disk-backed scan (ensure the read frame is allocated so frameSize > 0) so evicted pages are read back.
- 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
- Size MemorySize to hold the entire range you intend to scan.
- Always clamp scan start addresses to current HeadAddress for memory scans.
- Use a disk-backed scan (frameSize > 0) when the range may have spilled to disk.
- Persist and resume scans by address only when you know the range stays resident.
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
- Iterator address is less than log HeadAddress in memory-scan
- estimatedRecordSize ({estimatedTotalSize}) exceeds max alloc
- Page read from storage failed, skipping page. Inner exceptio
- Cannot cast a DiskLogRecord to a memory LogRecord.
- TsavoriteLogAllocator Scan methods should not be used
AI-assisted analysis of microsoft/garnet@951b0fc683 (2026-08-13).
Data as JSON: /api/errors/466508f0f4604bbe.
Report an issue: GitHub.