microsoft/garnet · error · InvalidOperationException
FlushAndReset is not supported for DiskStreamReadBuffer
Error message
FlushAndReset is not supported for DiskStreamReadBuffer
What it means
DiskStreamReadBuffer is a read-only buffer used to surface object-log bytes from a disk stream during reads. Its FlushAndReset implementation deliberately throws InvalidOperationException because a read buffer holds no writable/flushable state. Calling FlushAndReset on it is a programming error — the caller invoked a write-side operation on a read-side object.
Source
Thrown at libs/storage/Tsavorite/cs/src/core/Allocator/ObjectSerialization/ObjectLogReader.cs:69
/// </summary>
/// <param name="filePosition">The initial file position to read</param>
/// <param name="totalLength">The cumulative length of all object-log entries for the span of records to be read. We read ahead for all record
/// in the ReadAsync call.</param>
internal void OnBeginReadRecords(ObjectLogFilePositionInfo filePosition, ulong totalLength)
{
inDeserialize = false;
deserializedLength = 0UL;
readBuffers.OnBeginReadRecords(filePosition, totalLength);
}
/// <summary>
/// Called when one or more records with Objects have been read and via ReadAsync, e.g. being processed by AsyncReadPageWithObjectsCallback,
/// and we have completed reading and deserializing those objects.
/// </summary>
internal void OnEndReadRecords() => readBuffers.OnEndReadRecords();
/// <inheritdoc/>
public void FlushAndReset(CancellationToken cancellationToken = default) => throw new InvalidOperationException("FlushAndReset is not supported for DiskStreamReadBuffer");
/// <inheritdoc/>
public void Write(ReadOnlySpan<byte> data, CancellationToken cancellationToken = default) => throw new InvalidOperationException("Write is not supported for DiskStreamReadBuffer");
/// <summary>
/// Get the object log entries for Overflow Keys and Values and Object Values for the input <paramref name="logRecord"/>. We do not create the log record here;
/// that was already done by the caller from a single-record disk IO or from Recovery.
/// <list type="bullet">
/// <item>If there is an Overflow key, read it and if we have a <paramref name="requestedKey"/> compare it and return false if it does not match.
/// Otherwise, store the Key Overflow in the transient <see cref="ObjectIdMap"/> in <paramref name="logRecord"/>.
/// If we don't have <paramref name="requestedKey"/>, this is either ReadAtAddress (which is an implicit match) or Scan or Restore.</item>
/// <item>If we have an Overflow or Object value, read and store it in the transient <see cref="ObjectIdMap"/> in <paramref name="logRecord"/>.</item>
/// </list>
/// </summary>
/// <param name="logRecord">The initial record read from disk from Pending IO, so it is of size <see cref="IStreamBuffer.DefaultInitialIORecordSize"/> or less.</param>
/// <param name="requestedKey">The requested key, if not ReadAtAddress; we will compare to see if it matches the record.</param>
/// <param name="segmentSizeBits">Number of bits in segment size</param>
/// <returns>False if requestedKey is set and we read an Overflow key and it did not match; otherwise true</returns>View on GitHub (pinned to 951b0fc683)
Solutions
- Before calling FlushAndReset, check the buffer type (e.g. 'is DiskStreamReadBuffer' / a CanWrite flag) and skip the call for read buffers.
- Ensure the correct buffer kind is passed to the call site — read buffers for read paths, write buffers for write paths.
- Restructure so read and write buffer interfaces separate the FlushAndReset capability.
Example fix
// before
buffer.FlushAndReset(cancellationToken);
// after
if (buffer is not DiskStreamReadBuffer)
buffer.FlushAndReset(cancellationToken); Defensive patterns
Strategy: type-guard
Validate before calling
if (buffer is DiskStreamReadBuffer) throw new InvalidOperationException("FlushAndReset not valid for read buffers");
buffer.FlushAndReset(cancellationToken); Type guard
static bool CanFlushAndReset(IStreamBuffer b) => b is not DiskStreamReadBuffer;
Prevention
- Use separate pools/types for read vs write buffers.
- Check buffer direction before invoking write-only operations.
When it happens
Trigger: Code that holds an IStreamBuffer reference and unconditionally calls FlushAndReset, but the instance is actually a DiskStreamReadBuffer (returned for read paths such as ReadRecordObjects / disk-based object reads).
Common situations: Generic stream-buffer handling code that treats read and write buffers uniformly; a refactor that routed a read buffer into a path expecting a write buffer; misrouted serialization/deserialization buffer.
Related errors
- Write is not supported for DiskStreamReadBuffer
- Read is not supported for DiskStreamWriteBuffer
- {nameof(settings.LogSettings.NumberOfFlushBuffers)} must be
- {nameof(settings.LogSettings.NumberOfDeserializationBuffers)
- {nameof(settings.LogSettings.ObjectLogSegmentSizeBits)} must
AI-assisted analysis of microsoft/garnet@951b0fc683 (2026-08-13).
Data as JSON: /api/errors/a7e27a1c22b38a8d.
Report an issue: GitHub.