microsoft/garnet · error · NotSupportedException

Stream does not support get_Position.

Error message

Stream does not support get_Position.

What it means

PinnedMemoryStream is forward-only and chunked, so it cannot report a seekable Position; the Position getter throws NotSupportedException. Position only has meaning for seekable streams, which this is not.

Source

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

                streamBuffer.FlushAndReset(cancellationToken);
                return Task.CompletedTask;
            }
            catch (Exception ex)
            {
                return Task.FromException(ex);
            }
        }

        /// <summary>The amount of data in the internal streamBuffer. Not supported because we chunk and thus may not have all data.</summary>
        public override long Length
        {
            get => throw new NotSupportedException("Stream does not support get_Length.");
        }

        /// <summary>The current position of the stream seeking; not supported</summary>
        public override long Position
        {
            get => throw new NotSupportedException("Stream does not support get_Position.");
            set => throw new NotSupportedException("Stream does not support set_Position.");
        }

        /// <summary>Copy data from the internal streamBuffer into the buffer; the streamBuffer handles Flush, Reset, and Read more 
        /// (e.g. from disk or network) as needed.</summary>
        /// <param name="buffer">Buffer to copy the bytes into.</param>
        /// <param name="offset">Index in the buffer to start copying to.</param>
        /// <param name="count">Desired number of bytes to copy to the buffer.</param>
        /// <returns>Number of bytes actually read.</returns>
        public override int Read(byte[] buffer, int offset, int count)
        {
            ValidateBufferArguments(buffer, offset, count);
            return streamBuffer.Read(new Span<byte>(buffer, offset, count));
        }

        /// <summary>Copy data from the internal streamBuffer into the destination span; the streamBuffer handles Flush, Reset, and Read more 
        /// (e.g. from disk or network) as needed.</summary>
        public override int Read(Span<byte> destinationSpan) => streamBuffer.Read(destinationSpan);

View on GitHub (pinned to 951b0fc683)

Solutions

  1. Maintain your own byte counter instead of reading stream.Position.
  2. Avoid components that require Position on the object-log serialization stream.
  3. If Position is unavoidable, serialize into a MemoryStream and copy forward, accepting the trade-off.

Example fix

// before
var pos = w.BaseStream.Position; // throws
// after
long written = 0; written += sizeof(int); w.Write(_x);
Defensive patterns

Strategy: validation

Validate before calling

if (stream is PinnedMemoryStream) throw new NotSupportedException("PinnedMemoryStream has no Position; track bytes manually");
var pos = stream.Position;

Type guard

static bool HasPosition(Stream s) => s is not PinnedMemoryStream;

Prevention

When it happens

Trigger: Any code that reads the Position of a stream backed by PinnedMemoryStream — serializers, framing helpers, or logging code that accesses BaseStream.Position.

Common situations: A custom serializer recording offsets via stream.Position; wrapping the stream in an API that probes Position; copy code that uses Position to track progress.

Related errors


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