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.

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.

Appendix: 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)