microsoft/garnet · error · NotImplementedException

GetUpsertFieldInfo(ReadOnlySpan<byte> value) is not supporte

Error message

GetUpsertFieldInfo(ReadOnlySpan<byte> value) is not supported in this ISessionFunctions implementation

What it means

GetUpsertFieldInfo returns RecordFieldInfo (key size, value size, object flags) for sizing an upsert record. NoOpSessionFunctions only implements the ISourceLogRecord-based overload (line 95-101) because compaction copies records from source log records. The ReadOnlySpan<byte> overload throws NotImplementedException because compaction never upserts a raw span value — it always has a source record to copy from.

Source

Thrown at libs/storage/Tsavorite/cs/src/core/ClientSession/NoOpSessionFunctions.cs:88

        public readonly void ReadCompletionCallback(ref DiskLogRecord diskLogRecord, ref TInput input, ref TOutput output, TContext ctx, Status status, RecordMetadata recordMetadata) { }

        public readonly void RMWCompletionCallback(ref DiskLogRecord diskLogRecord, ref TInput input, ref TOutput output, TContext ctx, Status status, RecordMetadata recordMetadata) { }

        public readonly RecordFieldInfo GetRMWModifiedFieldInfo<TSourceLogRecord>(in TSourceLogRecord srcLogRecord, ref TInput input)
            where TSourceLogRecord : ISourceLogRecord
             => throw new NotImplementedException("GetRMWModifiedFieldInfo is not supported in this ISessionFunctions implementation");
        public readonly RecordFieldInfo GetRMWInitialFieldInfo<TKey>(TKey key, ref TInput input)
            where TKey : IKey
#if NET9_0_OR_GREATER
                , allows ref struct
#endif
            => throw new NotImplementedException("GetRMWInitialFieldInfo is not supported in this ISessionFunctions implementation");
        public readonly RecordFieldInfo GetUpsertFieldInfo<TKey>(TKey key, ReadOnlySpan<byte> value, ref TInput input)
            where TKey : IKey
#if NET9_0_OR_GREATER
                , allows ref struct
#endif
            => throw new NotImplementedException("GetUpsertFieldInfo(ReadOnlySpan<byte> value) is not supported in this ISessionFunctions implementation");
        public readonly RecordFieldInfo GetUpsertFieldInfo<TKey>(TKey key, IHeapObject value, ref TInput input)
            where TKey : IKey
#if NET9_0_OR_GREATER
                , allows ref struct
#endif
            => throw new NotImplementedException("IHeapObject value) is not supported in this ISessionFunctions implementation");
        public readonly RecordFieldInfo GetUpsertFieldInfo<TKey, TSourceLogRecord>(TKey key, in TSourceLogRecord inputLogRecord, ref TInput input)
            where TKey : IKey
#if NET9_0_OR_GREATER
                , allows ref struct
#endif
            where TSourceLogRecord : ISourceLogRecord
            => new() { KeySize = key.KeyBytes.Length, ValueSize = inputLogRecord.DataHeader.ValueIsObject ? ObjectIdMap.ObjectIdSize : inputLogRecord.ValueSpan.Length, ValueIsObject = inputLogRecord.DataHeader.ValueIsObject, ExtendedNamespaceSize = RecordNamespace.GetExtendedNamespaceSize(in key) };

        /// <summary>
        /// No reads during compaction
        /// </summary>
        public readonly bool Reader<TSourceLogRecord>(in TSourceLogRecord srcLogRecord, ref TInput input, ref TOutput dst, ref ReadInfo readInfo)

View on GitHub (pinned to 951b0fc683)

Solutions

  1. Report as a Tsavorite framework bug — compaction should use the ISourceLogRecord overload of GetUpsertFieldInfo, not the span-value one.
  2. If implementing custom ISessionFunctions, implement all GetUpsertFieldInfo overloads including the span-based one.
  3. Check whether the error correlates with specific record types or sizes that trigger a non-copy compaction path.
Defensive patterns

Strategy: try-catch

Try / catch

try
{
    store.Log.ShiftHeadAddressForCompaction(targetAddress);
}
catch (NotImplementedException ex) when (ex.Message.Contains("GetUpsertFieldInfo"))
{
    logger.LogCritical("Compaction hit unsupported span-value GetUpsertFieldInfo path: {Message}", ex.Message);
    throw;
}

Prevention

When it happens

Trigger: Tsavorite's internal code calls GetUpsertFieldInfo(TKey, ReadOnlySpan<byte> value, ref TInput) on a NoOpSessionFunctions session during compaction, meaning a raw-value upsert path was entered instead of the record-copy path.

Common situations: Framework bug in compaction dispatch routing a span-value upsert through a NoOp session; custom compaction logic that calls Upsert with a raw span value during a context backed by NoOpSessionFunctions.

Related errors


AI-assisted analysis of microsoft/garnet@951b0fc683 (2026-08-13). Data as JSON: /api/errors/8010a79a81cbfeb2. Report an issue: GitHub.