microsoft/garnet · error · NotImplementedException

InPlaceWriter(ReadOnlySpan<byte> value) is not supported in

Error message

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

What it means

NoOpSessionFunctions is an internal struct used by Tsavorite during compaction, iteration, and other internal operations that manipulate LogRecords directly rather than going through user-supplied callback values. It only implements the ISourceLogRecord-based overloads of InPlaceWriter/InitialWriter (for copying log records byte-for-byte). The ReadOnlySpan<byte> and IHeapObject overloads of InPlaceWriter throw NotImplementedException because compaction never upserts a raw span or heap object value — it always copies from a source log record.

Source

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

    /// <remarks>
    /// Because this is used for copy operations, the <see cref="GetUpsertFieldInfo{TKey, TSourceLogRecord}(TKey, in TSourceLogRecord, ref TInput)"/>,
    /// <see cref="InitialWriter{TSourceLogRecord}(ref LogRecord, in RecordSizeInfo, ref TInput, in TSourceLogRecord, ref TOutput, ref UpsertInfo)"/>, and
    /// <see cref="InPlaceWriter{TSourceLogRecord}(ref LogRecord, ref TInput, in TSourceLogRecord, ref TOutput, ref UpsertInfo)"/>, and
    /// methods are implemented to allow for copy of log records via Upsert, but no other methods are implemented.
    /// </remarks>
    /// <typeparam name="TInput"></typeparam>
    /// <typeparam name="TOutput"></typeparam>
    /// <typeparam name="TContext"></typeparam>
    internal struct NoOpSessionFunctions<TInput, TOutput, TContext> : ISessionFunctions<TInput, TOutput, TContext>
    {
        public readonly bool InitialDeleter(ref LogRecord logRecord, ref DeleteInfo deleteInfo) => true;

        public readonly void PostInitialDeleter(ref LogRecord logRecord, ref DeleteInfo deleteInfo) { }

        public readonly bool InPlaceDeleter(ref LogRecord logRecord, ref DeleteInfo deleteInfo) => true;

        public readonly bool InPlaceWriter(ref LogRecord logRecord, ref TInput input, ReadOnlySpan<byte> srcValue, ref TOutput output, ref UpsertInfo upsertInfo)
            => throw new NotImplementedException("InPlaceWriter(ReadOnlySpan<byte> value) is not supported in this ISessionFunctions implementation");

        public readonly bool InPlaceWriter(ref LogRecord logRecord, ref TInput input, IHeapObject srcValue, ref TOutput output, ref UpsertInfo upsertInfo)
            => throw new NotImplementedException("InPlaceWriter(IHeapObject value) is not supported in this ISessionFunctions implementation");

        public readonly bool InPlaceWriter<TSourceLogRecord>(ref LogRecord dstLogRecord, ref TInput input, in TSourceLogRecord inputLogRecord, ref TOutput output, ref UpsertInfo upsertInfo)
            where TSourceLogRecord : ISourceLogRecord
        {
            // This includes ETag and Expiration
            var sizeInfo = new RecordSizeInfo() { FieldInfo = GetUpsertFieldInfo(key: dstLogRecord, inputLogRecord, ref input) };
            dstLogRecord.PopulateRecordSizeInfoForIPU(ref sizeInfo);
            return dstLogRecord.TryCopyFrom(in inputLogRecord, in sizeInfo);
        }

        public readonly bool CopyUpdater<TSourceLogRecord>(in TSourceLogRecord srcLogRecord, ref LogRecord dstLogRecord, in RecordSizeInfo sizeInfo, ref TInput input, ref TOutput output, ref RMWInfo rmwInfo)
            where TSourceLogRecord : ISourceLogRecord
            => true;

        public readonly bool PostCopyUpdater<TSourceLogRecord>(in TSourceLogRecord srcLogRecord, ref LogRecord dstLogRecord, in RecordSizeInfo sizeInfo, ref TInput input, ref TOutput output, ref RMWInfo rmwInfo)

View on GitHub (pinned to 951b0fc683)

Solutions

  1. Report this as a Tsavorite framework bug — the compaction code path should never call the span-based InPlaceWriter on a NoOpSessionFunctions instance.
  2. If using a custom session functions implementation, ensure all InPlaceWriter overloads are implemented, not just the ISourceLogRecord one.
  3. Try disabling or adjusting compaction settings to see if the issue is data-dependent (e.g. record sizes that trigger a different copy path).
Defensive patterns

Strategy: try-catch

Try / catch

// If you suspect compaction may trigger this, catch NotImplementedException
// and log details to report as a framework bug
try
{
    store.Log.ShiftHeadAddressForCompaction(targetAddress);
}
catch (NotImplementedException ex) when (ex.Message.Contains("InPlaceWriter"))
{
    logger.LogCritical("Tsavorite compaction hit unsupported InPlaceWriter path: {Message}", ex.Message);
    throw;
}

Prevention

When it happens

Trigger: Tsavorite's internal compaction or checkpoint code path calls InPlaceWriter(ref LogRecord, ref TInput, ReadOnlySpan<byte> srcValue, ...) on a session using NoOpSessionFunctions. This indicates the compaction pipeline routed an upsert through the span-value path instead of the record-copy path. Since NoOpSessionFunctions is internal, external users cannot construct it directly — this error surfaces from within Tsavorite's own code during compaction.

Common situations: A Tsavorite framework bug in the compaction/copy-forward logic that incorrectly dispatches to the span-based InPlaceWriter; a custom checkpoint or compaction hook that triggers an upsert with a raw value during a NoOp session; upgrading Tsavorite to a version with a regression in compaction dispatch.

Related errors


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