{"record":{"id":"2aae53b34ec374a5","repo":"microsoft/garnet","slug":"write-is-not-supported-for-diskstreamreadbuffer","errorCode":null,"errorMessage":"Write is not supported for DiskStreamReadBuffer","messagePattern":"Write is not supported for DiskStreamReadBuffer","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"libs/storage/Tsavorite/cs/src/core/Allocator/ObjectSerialization/ObjectLogReader.cs","lineNumber":72,"sourceCode":"        ///     in the ReadAsync call.</param>\n        internal void OnBeginReadRecords(ObjectLogFilePositionInfo filePosition, ulong totalLength)\n        {\n            inDeserialize = false;\n            deserializedLength = 0UL;\n            readBuffers.OnBeginReadRecords(filePosition, totalLength);\n        }\n\n        /// <summary>\n        /// Called when one or more records with Objects have been read and via ReadAsync, e.g. being processed by AsyncReadPageWithObjectsCallback,\n        /// and we have completed reading and deserializing those objects.\n        /// </summary>\n        internal void OnEndReadRecords() => readBuffers.OnEndReadRecords();\n\n        /// <inheritdoc/>\n        public void FlushAndReset(CancellationToken cancellationToken = default) => throw new InvalidOperationException(\"FlushAndReset is not supported for DiskStreamReadBuffer\");\n\n        /// <inheritdoc/>\n        public void Write(ReadOnlySpan<byte> data, CancellationToken cancellationToken = default) => throw new InvalidOperationException(\"Write is not supported for DiskStreamReadBuffer\");\n\n        /// <summary>\n        /// Get the object log entries for Overflow Keys and Values and Object Values for the input <paramref name=\"logRecord\"/>. We do not create the log record here;\n        /// that was already done by the caller from a single-record disk IO or from Recovery.\n        /// <list type=\"bullet\">\n        /// <item>If there is an Overflow key, read it and if we have a <paramref name=\"requestedKey\"/> compare it and return false if it does not match.\n        ///     Otherwise, store the Key Overflow in the transient <see cref=\"ObjectIdMap\"/> in <paramref name=\"logRecord\"/>.\n        ///     If we don't have <paramref name=\"requestedKey\"/>, this is either ReadAtAddress (which is an implicit match) or Scan or Restore.</item>\n        /// <item>If we have an Overflow or Object value, read and store it in the transient <see cref=\"ObjectIdMap\"/> in <paramref name=\"logRecord\"/>.</item>\n        /// </list>\n        /// </summary>\n        /// <param name=\"logRecord\">The initial record read from disk from Pending IO, so it is of size <see cref=\"IStreamBuffer.DefaultInitialIORecordSize\"/> or less.</param>\n        /// <param name=\"requestedKey\">The requested key, if not ReadAtAddress; we will compare to see if it matches the record.</param>\n        /// <param name=\"segmentSizeBits\">Number of bits in segment size</param>\n        /// <returns>False if requestedKey is set and we read an Overflow key and it did not match; otherwise true</returns>\n        [MethodImpl(MethodImplOptions.NoInlining)]\n        public bool ReadRecordObjects<TKey>(ref LogRecord logRecord, TKey requestedKey, int segmentSizeBits)\n            where TKey : IKey","sourceCodeStart":54,"sourceCodeEnd":90,"githubUrl":"https://github.com/microsoft/garnet/blob/951b0fc6838721f89d102c2bbe1b914e8d39d700/libs/storage/Tsavorite/cs/src/core/Allocator/ObjectSerialization/ObjectLogReader.cs#L54-L90","documentation":"DiskStreamReadBuffer is read-only; its Write implementation throws InvalidOperationException. Writing into a read buffer is meaningless because its contents come from disk IO, not from the caller. The throw guards against misuse where a read buffer is handed to a write path.","triggerScenarios":"Calling Write(ReadOnlySpan<byte>) on an IStreamBuffer that is actually a DiskStreamReadBuffer — e.g. serializing objects into a buffer that was allocated for the object-log read path.","commonSituations":"Shared buffer-pool code that blindly calls Write on whichever buffer it pulls; a code path that obtains a read buffer but then attempts to serialize into it; misconfigured buffer pool returning read buffers to writers.","solutions":["Verify the buffer is writable before calling Write (type-check or a CanWrite flag).","Use separate pools for read and write buffers so a writer can never receive a DiskStreamReadBuffer.","Route the operation through the correct buffer type for its direction."],"exampleFix":"// before\nbuffer.Write(data, cancellationToken);\n// after\nif (buffer is DiskStreamReadBuffer)\n    throw new InvalidOperationException(\"Cannot write to a read buffer\");\nbuffer.Write(data, cancellationToken);","handlingStrategy":"type-guard","validationCode":"if (buffer is DiskStreamReadBuffer) throw new InvalidOperationException(\"Cannot Write to a read buffer\");\nbuffer.Write(data, cancellationToken);","typeGuard":"static bool CanWrite(IStreamBuffer b) => b is not DiskStreamReadBuffer;","tryCatchPattern":null,"preventionTips":["Never pull from a shared pool that mixes read and write buffers for write operations.","Gate Write behind a type/capability check."],"tags":["object-log","stream-buffer","read-only","invalid-operation"],"backgroundTag":null,"analyzedSha":"951b0fc6838721f89d102c2bbe1b914e8d39d700","analyzedAt":"2026-08-13T19:01:32.939Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}