{"record":{"id":"abf5676ce3051d0e","repo":"microsoft/garnet","slug":"stream-does-not-support-seek","errorCode":null,"errorMessage":"Stream does not support Seek.","messagePattern":"Stream does not support Seek\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"libs/storage/Tsavorite/cs/src/core/Allocator/ObjectSerialization/PinnedMemoryStream.cs","lineNumber":124,"sourceCode":"            {\n                return new ValueTask<int>(Read(buffer.Span));\n            }\n            catch (Exception ex)\n            {\n                return ValueTask.FromException<int>(ex);\n            }\n        }\n\n        /// <summary>Returns the byte at the current streamBuffer position and advances the position</summary>\n        /// <returns>The byte read (as an int)</returns>\n        public override unsafe int ReadByte()\n        {\n            byte b = default;\n            return streamBuffer.Read(new Span<byte>(ref b)) > 0 ? b : -1;\n        }\n\n        /// <summary>Seeking is not supported in this stream.</summary>\n        public override long Seek(long offset, SeekOrigin loc) => throw new InvalidOperationException(\"Stream does not support Seek.\");\n\n        /// <summary>Seeking is not supported in this stream.</summary>\n        public override void SetLength(long value) => throw new InvalidOperationException(\"Stream does not support SetLength.\");\n\n        /// <summary>Write the buffer to the stream; the streamBuffer handles Flush, Reset, and Writing iteratively \n        /// (e.g. to disk or network) as needed.</summary>\n        /// <param name=\"buffer\">Buffer to write the bytes from.</param>\n        /// <param name=\"offset\">Index in the buffer to start writing from.</param>\n        /// <param name=\"count\">Desired number of bytes to write from the buffer.</param>\n        public override void Write(byte[] buffer, int offset, int count)\n        {\n            ValidateBufferArguments(buffer, offset, count);\n            streamBuffer.Write(new ReadOnlySpan<byte>(buffer, offset, count));\n        }\n\n        /// <summary>Write the buffer to the stream; the streamBuffer handles Flush, Reset, and Writing iteratively \n        /// (e.g. to disk or network) as needed.</summary>\n        public override void Write(ReadOnlySpan<byte> destinationSpan) => streamBuffer.Write(destinationSpan);","sourceCodeStart":106,"sourceCodeEnd":142,"githubUrl":"https://github.com/microsoft/garnet/blob/951b0fc6838721f89d102c2bbe1b914e8d39d700/libs/storage/Tsavorite/cs/src/core/Allocator/ObjectSerialization/PinnedMemoryStream.cs#L106-L142","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Write length prefixes without seeking: reserve space by buffering the record first, or compute the length before writing.","Do not call Seek on the object-log serialization stream.","If a downstream component must seek, serialize into a MemoryStream and then copy the bytes forward into the Tsavorite stream."],"exampleFix":"// before\nlong pos = w.BaseStream.Position;\nw.Write(payload);\nw.BaseStream.Seek(pos, SeekOrigin.Begin); // throws\n// after\nw.Write(payload.Length); w.Write(payload);","handlingStrategy":"validation","validationCode":"if (!stream.CanSeek) throw new NotSupportedException(\"Stream is not seekable; use forward-only writes\");\nstream.Seek(offset, origin);","typeGuard":"static bool CanSeekSafe(Stream s) => s.CanSeek;","tryCatchPattern":null,"preventionTips":["Write length prefixes without seeking: buffer first or compute length before writing.","Check CanSeek before any Seek call on a stream you don't own.","Avoid patching headers by rewinding on the object-log serialization stream."],"tags":["object-log","stream","not-supported","serialization","seek"],"backgroundTag":null,"analyzedSha":"951b0fc6838721f89d102c2bbe1b914e8d39d700","analyzedAt":"2026-08-13T19:01:32.939Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}