{"record":{"id":"a7e27a1c22b38a8d","repo":"microsoft/garnet","slug":"flushandreset-is-not-supported-for-diskstreamreadb","errorCode":null,"errorMessage":"FlushAndReset is not supported for DiskStreamReadBuffer","messagePattern":"FlushAndReset 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":69,"sourceCode":"        /// </summary>\n        /// <param name=\"filePosition\">The initial file position to read</param>\n        /// <param name=\"totalLength\">The cumulative length of all object-log entries for the span of records to be read. We read ahead for all record\n        ///     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>","sourceCodeStart":51,"sourceCodeEnd":87,"githubUrl":"https://github.com/microsoft/garnet/blob/951b0fc6838721f89d102c2bbe1b914e8d39d700/libs/storage/Tsavorite/cs/src/core/Allocator/ObjectSerialization/ObjectLogReader.cs#L51-L87","documentation":"DiskStreamReadBuffer is a read-only buffer used to surface object-log bytes from a disk stream during reads. Its FlushAndReset implementation deliberately throws InvalidOperationException because a read buffer holds no writable/flushable state. Calling FlushAndReset on it is a programming error — the caller invoked a write-side operation on a read-side object.","triggerScenarios":"Code that holds an IStreamBuffer reference and unconditionally calls FlushAndReset, but the instance is actually a DiskStreamReadBuffer (returned for read paths such as ReadRecordObjects / disk-based object reads).","commonSituations":"Generic stream-buffer handling code that treats read and write buffers uniformly; a refactor that routed a read buffer into a path expecting a write buffer; misrouted serialization/deserialization buffer.","solutions":["Before calling FlushAndReset, check the buffer type (e.g. 'is DiskStreamReadBuffer' / a CanWrite flag) and skip the call for read buffers.","Ensure the correct buffer kind is passed to the call site — read buffers for read paths, write buffers for write paths.","Restructure so read and write buffer interfaces separate the FlushAndReset capability."],"exampleFix":"// before\nbuffer.FlushAndReset(cancellationToken);\n// after\nif (buffer is not DiskStreamReadBuffer)\n    buffer.FlushAndReset(cancellationToken);","handlingStrategy":"type-guard","validationCode":"if (buffer is DiskStreamReadBuffer) throw new InvalidOperationException(\"FlushAndReset not valid for read buffers\");\nbuffer.FlushAndReset(cancellationToken);","typeGuard":"static bool CanFlushAndReset(IStreamBuffer b) => b is not DiskStreamReadBuffer;","tryCatchPattern":null,"preventionTips":["Use separate pools/types for read vs write buffers.","Check buffer direction before invoking write-only operations."],"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"}