microsoft/garnet · error · InvalidOperationException

Stream does not support Seek.

Error message

Stream does not support Seek.

What it means

PinnedMemoryStream is non-seekable: it streams data through an IStreamBuffer in chunks, so seeking to an arbitrary offset is impossible. Its Seek override throws InvalidOperationException. The stream is forward-only by construction.

Source

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

            {
                return new ValueTask<int>(Read(buffer.Span));
            }
            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);

View on GitHub (pinned to 951b0fc683)

Solutions

  1. Write length prefixes without seeking: reserve space by buffering the record first, or compute the length before writing.
  2. Do not call Seek on the object-log serialization stream.
  3. If a downstream component must seek, serialize into a MemoryStream and then copy the bytes forward into the Tsavorite stream.

Example fix

// before
long pos = w.BaseStream.Position;
w.Write(payload);
w.BaseStream.Seek(pos, SeekOrigin.Begin); // throws
// after
w.Write(payload.Length); w.Write(payload);
Defensive patterns

Strategy: validation

Validate before calling

if (!stream.CanSeek) throw new NotSupportedException("Stream is not seekable; use forward-only writes");
stream.Seek(offset, origin);

Type guard

static bool CanSeekSafe(Stream s) => s.CanSeek;

Prevention

When it happens

Trigger: Code that calls Seek on a PinnedMemoryStream-backed stream — e.g. a serializer/framework that rewinds, length-prefixed framing that seeks back to patch a header, or a copy routine that seeks.

Common situations: A custom IHeapObject serializer that seeks back to write a length prefix; wrapping PinnedMemoryStream in a component that rewinds (compression, crypto with seek); a BinaryWriter pattern that seeks to finalize headers.

Related errors


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