microsoft/FASTER · critical · FasterException
Attempting to return an already-free block
Error message
Attempting to return an already-free block
What it means
SectorAlignedBufferPool blocks carry a free bit (kFreeBitMask) in their level field. The Free property setter validates transitions: marking a block free when it is already free throws "Attempting to return an already-free block" (a double-free), and marking an allocated block as allocated throws the symmetric error. This indicates the pool's block bookkeeping is corrupted - the same memory sector was released twice or ownership was mishandled.
Solutions
- Audit code that obtains SectorAlignedMemory from FASTER APIs and ensure Dispose/Return is called exactly once per acquisition.
- Do not share pool-allocated buffers across threads or keep copies of the struct after disposing it.
- If the buffer was returned by a library API (e.g. log scan), let the library manage its lifetime - do not dispose or return it yourself.
- If it occurs inside the library with single-threaded, correct usage, report a bug with the call stack.
Example fix
// before var buf = allocator.GetBuffer(); buf.Return(); // ... later in cleanup buf.Return(); // double free // after var buf = allocator.GetBuffer(); buf.Return(); buf.ReturnPool = false; // or drop the reference after first Return()
Defensive patterns
Strategy: try-catch
Validate before calling
// Track buffer ownership explicitly; return each buffer exactly once
private readonly HashSet<SectorAlignedMemory> returned = new();
void SafeReturn(SectorAlignedMemory buf)
{
if (!returned.Add(buf)) throw new InvalidOperationException("Buffer already returned");
buf.Return();
} Type guard
bool CanReturn(SectorAlignedMemory buf) => !buf.Free; // Free == already returned to the pool
Try / catch
try
{
buf.Return();
}
catch (FasterException ex) when (ex.Message.Contains("already-free block"))
{
logger.LogError(ex, "Double-free of sector-aligned buffer detected; ownership bug in caller");
} Prevention
- Return each pool buffer exactly once; use flags or ownership wrappers to enforce it.
- Never dispose or return buffers owned by the library (e.g. log scan results).
- Avoid sharing SectorAlignedMemory across threads without synchronization.
- Check the Free property before any manual Return() call.
When it happens
Trigger: Internal double-dispose of sector-aligned buffers: calling Dispose/Return twice on the same buffer object, sharing a buffer between threads without synchronization, or a library bug where the same SectorAlignedMemory is returned to the pool by two code paths.
Common situations: User code reusing or disposing buffers obtained from FasterLog/FASTER allocators after ownership transferred back to the library; wrapping pool buffers in another disposable that is disposed twice; use-after-free patterns where a stale buffer reference is returned.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- Attempting to allocate an already-allocated block
- Out of order message within session
- Unexpected status of SubscribeKV
- Cannot use BlittableParameterSerializer with non-blittable…
- The inner list is full!
AI-assisted analysis of microsoft/FASTER@321d872eab (2026-09-15).
Data as JSON: /api/errors/5e57c096a50e8e45.
Report an issue: GitHub.
Appendix: source
Thrown at cs/src/core/Utilities/BufferPool.cs:79
private int level;
internal int Level => this.level
#if CHECK_FREE
& ~kFreeBitMask
#endif
;
internal SectorAlignedBufferPool pool;
#if CHECK_FREE
internal bool Free
{
get => (level & kFreeBitMask) != 0;
set
{
if (value)
{
if (Free)
throw new FasterException("Attempting to return an already-free block");
this.level |= kFreeBitMask;
}
else
{
if (!Free)
throw new FasterException("Attempting to allocate an already-allocated block");
this.level &= ~kFreeBitMask;
}
}
}
#endif // CHECK_FREE
/// <summary>
/// Default constructor
/// </summary>
public SectorAlignedMemory(int level = default)
{
this.level = level;View on GitHub (pinned to 321d872eab)