microsoft/FASTER · error · FasterException
Cannot scan disposed log instance
Error message
Cannot scan disposed log instance
What it means
FasterLog uses a reference count (logRefCount) on the underlying log; a value of 0 after increment means the log instance has already been disposed. Scan therefore refuses to create an iterator on a disposed instance at FasterLog.cs:1842 to avoid operating on freed devices/memory.
Solutions
- Ensure the log instance is not disposed before all Scan calls finish; keep logRefCount held via a live reference.
- Fix shutdown ordering: cancel/await iterator consumer tasks before disposing the FasterLog.
- If the instance was disposed intentionally, create a new FasterLog from the same settings instead of scanning the old one.
Example fix
// before log.Dispose(); var iter = log.Scan(0, long.MaxValue); // after var iter = log.Scan(0, long.MaxValue); // consume... iter.Dispose(); log.Dispose();
Defensive patterns
Strategy: try-catch
Validate before calling
// track disposal in your wrapper if (Interlocked.Read(ref disposed) != 0) throw new ObjectDisposedException(nameof(FasterLog));
Try / catch
try { var iter = log.Scan(0, long.MaxValue); } catch (FasterException ex) when (ex.Message.Contains("disposed log instance")) { // recreate log or stop consuming
} Prevention
- Bind FasterLog lifetime to the host (e.g. IHostedService.StopAsync disposes last).
- Cancel iterator loops before disposal and await them.
- Wrap FasterLog in a class that guards all access with a disposed flag.
When it happens
Trigger: Calling FasterLog.Scan (with or without a name) after the log's Dispose() has been called, or concurrently racing Dispose against Scan while other references have been released.
Common situations: Application shutdown ordering issues where a consumer thread still calls Scan after the hosting service disposed the FasterLog; or reusing a cached disposed FasterLog singleton.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Lockable method call when BeginLockable has not been called
- BeginLockable cannot be called twice (call EndLockable…
- Cannot use scanUncommitted without setting…
- Max length of iterator name is 20 characters
- This method can only be used with a read-only FasterLog…
AI-assisted analysis of microsoft/FASTER@321d872eab (2026-09-15).
Data as JSON: /api/errors/99ae559958d916b5.
Report an issue: GitHub.
Appendix: source
Thrown at cs/src/core/FasterLog/FasterLog.cs:1842
throw new FasterException("Cannot use scanUncommitted without setting AutoRefreshSafeTailAddress to true in FasterLog settings");
FasterLogScanIterator iter;
if (recover && name != null && RecoveredIterators != null && RecoveredIterators.ContainsKey(name))
iter = new FasterLogScanIterator(this, allocator, RecoveredIterators[name], endAddress, getMemory, scanBufferingMode, epoch, headerSize, name, scanUncommitted, logger: logger);
else
iter = new FasterLogScanIterator(this, allocator, beginAddress, endAddress, getMemory, scanBufferingMode, epoch, headerSize, name, scanUncommitted, logger: logger);
if (name != null)
{
if (name.Length > 20)
throw new FasterException("Max length of iterator name is 20 characters");
if (PersistedIterators.ContainsKey(name))
logger?.LogDebug("Iterator name exists, overwriting");
PersistedIterators[name] = iter;
}
if (Interlocked.Increment(ref logRefCount) == 1)
throw new FasterException("Cannot scan disposed log instance");
return iter;
}
/// <summary>
/// Random read record from log, at given address
/// </summary>
/// <param name="address">Logical address to read from</param>
/// <param name="estimatedLength">Estimated length of entry, if known</param>
/// <param name="token">Cancellation token</param>
/// <returns></returns>
public async ValueTask<(byte[], int)> ReadAsync(long address, int estimatedLength = 0, CancellationToken token = default)
{
token.ThrowIfCancellationRequested();
epoch.Resume();
if (address >= CommittedUntilAddress || address < BeginAddress)
{
epoch.Suspend();
return default;View on GitHub (pinned to 321d872eab)