microsoft/garnet · error · InvalidOperationException

Stream does not support SetLength.

Error message

Stream does not support SetLength.

What it means

PinnedMemoryStream is a forward-only, fixed-capacity stream over a pre-allocated, pinned byte buffer used during Tsavorite object serialization (native pointers alias the buffer). SetLength is intentionally unsupported because the buffer is allocated once and pinned for the GC; resizing/truncating would break the pinned-pointer memory model. Seek is unsupported for the same reason.

Source

Thrown at libs/storage/Tsavorite/cs/src/core/Allocator/ObjectSerialization/PinnedMemoryStream.cs:127

            catch (Exception ex)
            {
                return ValueTask.FromException<int>(ex);
            }
        }

        /// <summary>Returns the byte at the current streamBuffer position and advances the position</summary>
        /// <returns>The byte read (as an int)</returns>
        public override unsafe int ReadByte()
        {
            byte b = default;
            return streamBuffer.Read(new Span<byte>(ref b)) > 0 ? b : -1;
        }

        /// <summary>Seeking is not supported in this stream.</summary>
        public override long Seek(long offset, SeekOrigin loc) => throw new InvalidOperationException("Stream does not support Seek.");

        /// <summary>Seeking is not supported in this stream.</summary>
        public override void SetLength(long value) => throw new InvalidOperationException("Stream does not support SetLength.");

        /// <summary>Write the buffer to the stream; the streamBuffer handles Flush, Reset, and Writing iteratively 
        /// (e.g. to disk or network) as needed.</summary>
        /// <param name="buffer">Buffer to write the bytes from.</param>
        /// <param name="offset">Index in the buffer to start writing from.</param>
        /// <param name="count">Desired number of bytes to write from the buffer.</param>
        public override void Write(byte[] buffer, int offset, int count)
        {
            ValidateBufferArguments(buffer, offset, count);
            streamBuffer.Write(new ReadOnlySpan<byte>(buffer, offset, count));
        }

        /// <summary>Write the buffer to the stream; the streamBuffer handles Flush, Reset, and Writing iteratively 
        /// (e.g. to disk or network) as needed.</summary>
        public override void Write(ReadOnlySpan<byte> destinationSpan) => streamBuffer.Write(destinationSpan);

        /// <summary>Asynchronously write the buffer to the stream; the streamBuffer handles Flush, Reset, and Writing iteratively 
        /// (e.g. to disk or network) as needed.</summary>

View on GitHub (pinned to 951b0fc683)

Solutions

  1. Do not call SetLength on PinnedMemoryStream; size the buffer correctly up front and recreate the stream when you need a fresh one.
  2. Guard the call with a capability check: PinnedMemoryStream returns false for CanSeek, so test CanSeek before SetLength.
  3. If you need a resizable/truncatable buffer, copy into a MemoryStream instead of aliasing the pinned buffer.
  4. Audit generic serialization helpers for unconditional SetLength/Seek calls before pointing them at pinned streams.

Example fix

// before
stream.SetLength(0);
stream.Position = 0;

// after (recreate instead of mutating)
stream.Dispose();
stream = new PinnedMemoryStream(new byte[capacity]);
Defensive patterns

Strategy: validation

Validate before calling

// PinnedMemoryStream returns CanSeek = false; only resize streams that advertise seek/length
if (stream.CanSeek && stream.CanWrite)
    stream.SetLength(desiredLength);
else
    stream = NewFreshPinnedStream(desiredCapacity);

Type guard

// Streams backed by fixed pinned memory are not length-mutable
static bool IsLengthMutable(Stream s) => s.CanSeek && s.CanWrite && s is not PinnedMemoryStream;

Prevention

When it happens

Trigger: Any code path calls stream.SetLength(value) on a PinnedMemoryStream instance. Typically a generic serializer, a copy/reset helper that calls SetLength(0) to clear, or a stream-wrapper that defensively resizes its target.

Common situations: Running a third-party serializer or BinaryFormatter-style copy routine over a PinnedMemoryStream; a 'reset stream' helper that truncates instead of recreating; wrapping the stream in a helper that abstracts over Stream and unconditionally mutates length.

Related errors


AI-assisted analysis of microsoft/garnet@951b0fc683 (2026-08-13). Data as JSON: /api/errors/77534d761caa454a. Report an issue: GitHub.