microsoft/garnet · error · InvalidOperationException
Write is not supported for DiskStreamReadBuffer
Error message
Write is not supported for DiskStreamReadBuffer
What it means
DiskStreamReadBuffer is read-only; its Write implementation throws InvalidOperationException. Writing into a read buffer is meaningless because its contents come from disk IO, not from the caller. The throw guards against misuse where a read buffer is handed to a write path.
Source
Thrown at libs/storage/Tsavorite/cs/src/core/Allocator/ObjectSerialization/ObjectLogReader.cs:72
/// 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>
[MethodImpl(MethodImplOptions.NoInlining)]
public bool ReadRecordObjects<TKey>(ref LogRecord logRecord, TKey requestedKey, int segmentSizeBits)
where TKey : IKeyView on GitHub (pinned to 951b0fc683)
Solutions
- Verify the buffer is writable before calling Write (type-check or a CanWrite flag).
- Use separate pools for read and write buffers so a writer can never receive a DiskStreamReadBuffer.
- Route the operation through the correct buffer type for its direction.
Example fix
// before
buffer.Write(data, cancellationToken);
// after
if (buffer is DiskStreamReadBuffer)
throw new InvalidOperationException("Cannot write to a read buffer");
buffer.Write(data, cancellationToken); Defensive patterns
Strategy: type-guard
Validate before calling
if (buffer is DiskStreamReadBuffer) throw new InvalidOperationException("Cannot Write to a read buffer");
buffer.Write(data, cancellationToken); Type guard
static bool CanWrite(IStreamBuffer b) => b is not DiskStreamReadBuffer;
Prevention
- Never pull from a shared pool that mixes read and write buffers for write operations.
- Gate Write behind a type/capability check.
When it happens
Trigger: Calling Write(ReadOnlySpan<byte>) on an IStreamBuffer that is actually a DiskStreamReadBuffer — e.g. serializing objects into a buffer that was allocated for the object-log read path.
Common situations: Shared buffer-pool code that blindly calls Write on whichever buffer it pulls; a code path that obtains a read buffer but then attempts to serialize into it; misconfigured buffer pool returning read buffers to writers.
Related errors
- FlushAndReset 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/2aae53b34ec374a5.
Report an issue: GitHub.