microsoft/garnet · error · TsavoriteException
ReadRecordObjects found no data available in ReadBuffers
Error message
ReadRecordObjects found no data available in ReadBuffers
What it means
Thrown by ObjectLogReader.ReadRecordObjects when readBuffers is non-null but OnBeginRecord returns false for the record's object-log position — the read-ahead window does not cover the requested record's position, or the buffers hold no data for it. The record references object-log bytes that the current buffered read does not contain.
Source
Thrown at libs/storage/Tsavorite/cs/src/core/Allocator/ObjectSerialization/ObjectLogReader.cs:104
/// <param name="segmentSizeBits">Number of bits in segment size</param>
/// <returns>False if requestedKey is set and we read an Overflow key and it did not match; otherwise true</returns>
[MethodImpl(MethodImplOptions.NoInlining)]
public bool ReadRecordObjects<TKey>(ref LogRecord logRecord, TKey requestedKey, int segmentSizeBits)
where TKey : IKey
#if NET9_0_OR_GREATER
, allows ref struct
#endif
{
Debug.Assert(logRecord.DataHeader.RecordHasObjects, "Inline records should have been checked by the caller");
Debug.Assert(logRecord.HasReuseObjectIdForSize, "ReadRecordObjects requires the ReuseObjectIdForSize flag to be set on the ObjectLogPosition");
if (readBuffers is null)
throw new TsavoriteException("ReadBuffers are required to ReadRecordObjects");
// R11 encoding: lengths returned by GetObjectLogRecordStartPositionAndLengths combine the RDH low bits with the next 32 bits
// from the int* slot at keyAddress/valueAddress. No length prefix is in the object stream.
var positionWord = logRecord.GetObjectLogRecordStartPositionAndLengths(out var keyLength, out var valueLength);
if (!readBuffers.OnBeginRecord(new ObjectLogFilePositionInfo(positionWord, segmentSizeBits)))
throw new TsavoriteException("ReadRecordObjects found no data available in ReadBuffers");
// TODO: Optimize the reading of large internal sector-aligned parts of Overflow Keys and Values to read directly into the overflow, similar to how ObjectLogWriter writes
// directly from overflow. This requires changing the read-ahead in CircularDiskReadBuffer.OnBeginReadRecords and the "backfill" in CircularDiskReadBuffer.MoveToNextBuffer.
// Note: Similar logic to this is in DiskLogRecord.Deserialize.
var keyWasSet = false;
try
{
if (logRecord.DataHeader.KeyIsOverflow)
{
// This assignment also allocates the slot in ObjectIdMap, overwriting the int* slot at keyAddress
// (which held the high 32 bits of the on-disk key length per R11). The raw RDH KeyLength is restored
// to ObjectIdSize by OnObjectReadComplete below.
logRecord.KeyOverflow = new OverflowByteArray(keyLength, startOffset: 0, endOffset: 0, zeroInit: false);
_ = Read(logRecord.KeyOverflow.Span);
if (!requestedKey.IsEmpty && !storeFunctions.KeysEqual(requestedKey, logRecord))
return false;
keyWasSet = true;View on GitHub (pinned to 951b0fc683)
Solutions
- Ensure OnBeginReadRecords is invoked with a filePosition/totalLength that covers the record before reading its objects (proper read-ahead setup).
- Increase the read-ahead window / NumberOfDeserializationBuffers so records stay buffered.
- Verify the record's object-log position word is within the segment currently loaded by the read buffer.
Example fix
// before var ok = readBuffers.OnBeginRecord(new ObjectLogFilePositionInfo(positionWord, segmentSizeBits)); // after: set up read-ahead covering the record before OnBeginRecord readBuffers.OnBeginReadRecords(filePosition, totalLength); var ok = readBuffers.OnBeginRecord(new ObjectLogFilePositionInfo(positionWord, segmentSizeBits));
Defensive patterns
Strategy: validation
Validate before calling
// Ensure read-ahead covers the record before reading objects
readBuffers.OnBeginReadRecords(recordFilePosition, recordTotalLength);
if (!readBuffers.OnBeginRecord(new ObjectLogFilePositionInfo(positionWord, segmentSizeBits)))
throw new InvalidOperationException("Read buffer does not cover this record's object-log position"); Try / catch
try { reader.ReadRecordObjects(ref logRecord, requestedKey, segmentSizeBits); }
catch (TsavoriteException ex) when (ex.Message.Contains("no data available in ReadBuffers"))
{
logger.LogError(ex, "Read-ahead window missed the record; reposition buffers and retry");
throw;
} Prevention
- Call OnBeginReadRecords with a range covering the record before OnBeginRecord.
- Size read-ahead/NumberOfDeserializationBuffers to keep records buffered.
When it happens
Trigger: Calling ReadRecordObjects for a record whose ObjectLogFilePositionInfo (from GetObjectLogRecordStartPositionAndLengths) falls outside what readBuffers.OnBeginReadRecords was set up for — e.g. read-ahead not advanced to this record, wrong segment, or a stale buffer position after recovery/seek.
Common situations: Read-ahead sizing too small for the record; the read buffer was positioned for a different record/segment; recovery copying where snapshot object reader wasn't demand-loaded for this range; a bug in buffer positioning leaving a gap.
Related errors
- ReadBuffers are required to ReadRecordObjects
- Exceeded maximum response size of ({Array.MaxLength:N0}) byt
- {nameof(settings.LogSettings.NumberOfFlushBuffers)} must be
- {nameof(settings.LogSettings.NumberOfDeserializationBuffers)
- {nameof(settings.LogSettings.ObjectLogSegmentSizeBits)} must
AI-assisted analysis of microsoft/garnet@951b0fc683 (2026-08-13).
Data as JSON: /api/errors/3a6178d21851614a.
Report an issue: GitHub.