microsoft/garnet · error · TsavoriteException

Invalid compaction type

Error message

Invalid compaction type

What it means

Compact<TInput,TOutput,TContext,TCompactionFunctions> (TsavoriteCompaction.cs:21) dispatches on a CompactionType enum switch; the default arm throws 'Invalid compaction type'. The public enum (CompactionType.cs) defines only Scan and Lookup, so this arm is effectively unreachable through normal C# calls and exists as an exhaustive-switch guard. It fires only when the enum carries an out-of-range value (e.g. an invalid cast from an integer).

Source

Thrown at libs/storage/Tsavorite/cs/src/core/Compaction/TsavoriteCompaction.cs:28

        where TStoreFunctions : IStoreFunctions
        where TAllocator : IAllocator<TStoreFunctions>
    {
        /// <summary>
        /// Compact the log until specified address, moving active records to the tail of the log. BeginAddress is shifted, but the physical log
        /// is not deleted from disk. Caller is responsible for truncating the physical log on disk by taking a checkpoint or calling Log.Truncate
        /// </summary>
        /// <param name="cf">User provided compaction functions (see <see cref="ICompactionFunctions"/>).</param>
        /// <param name="untilAddress">Compact log until this address</param>
        /// <param name="compactionType">Compaction type (whether we lookup records or scan log for liveness checking)</param>
        /// <returns>Address until which compaction was done</returns>
        internal long Compact<TInput, TOutput, TContext, TCompactionFunctions>(TCompactionFunctions cf, long untilAddress, CompactionType compactionType)
            where TCompactionFunctions : ICompactionFunctions
        {
            return compactionType switch
            {
                CompactionType.Scan => CompactScan<TInput, TOutput, TContext, TCompactionFunctions>(cf, untilAddress),
                CompactionType.Lookup => CompactLookup<TInput, TOutput, TContext, TCompactionFunctions>(cf, untilAddress),
                _ => throw new TsavoriteException("Invalid compaction type"),
            };
        }

        private long CompactLookup<TInput, TOutput, TContext, TCompactionFunctions>(TCompactionFunctions cf, long untilAddress)
            where TCompactionFunctions : ICompactionFunctions
        {
            if (untilAddress > hlogBase.SafeReadOnlyAddress)
                throw new TsavoriteException("Can compact only until Log.SafeReadOnlyAddress");

            using var storeSession = NewSession<ITsavoriteScanIterator, TInput, TOutput, TContext, NoOpSessionFunctions<TInput, TOutput, TContext>>(new());
            var storebContext = storeSession.BasicContext;

            using (var iter1 = Log.Scan(Log.BeginAddress, untilAddress))
            {
                long numPending = 0;
                while (iter1.GetNext())
                {
                    var key = iter1.Key;

View on GitHub (pinned to 951b0fc683)

Solutions

  1. Validate the CompactionType with Enum.IsDefined before calling Compact and reject/normalize unknown values at the trust boundary.
  2. Pass only CompactionType.Scan or CompactionType.Lookup from strongly-typed code; never cast an integer to CompactionType.
  3. If the value originates from config, map the string token to a known enum member explicitly.

Example fix

// before
var t = (CompactionType)configValue;            // could be 99
store.Log.Compact<TInput, TOutput, TContext>(untilAddress, t); // may throw 'Invalid compaction type'

// after
var t = Enum.IsDefined(typeof(CompactionType), configValue)
    ? (CompactionType)configValue
    : throw new ArgumentOutOfRangeException(nameof(configValue), $"Unknown CompactionType {configValue}");
store.Log.Compact<TInput, TOutput, TContext>(untilAddress, t);
Defensive patterns

Strategy: validation

Validate before calling

// Validate the enum at the trust boundary before calling Compact.
if (!Enum.IsDefined(typeof(CompactionType), compactionType))
    throw new ArgumentOutOfRangeException(nameof(compactionType),
        $"Unknown CompactionType {(int)compactionType}; expected Scan or Lookup");
store.Log.Compact<TInput, TOutput, TContext>(untilAddress, compactionType);

Type guard

static bool IsValid(CompactionType t) => Enum.IsDefined(typeof(CompactionType), t);
// Guard: if (!IsValid(t)) reject before Compact.

Prevention

When it happens

Trigger: Calling Compact with a CompactionType value that is neither Scan nor Lookup, e.g. session.BasicContext.Compact<...>((CompactionType)99, ...) or store.Log.Compact<...>(addr, (CompactionType)5). This requires an explicit invalid cast or deserialized enum value.

Common situations: A CompactionType is read from config/JSON/deserialization and an unrecognized token maps to an undefined numeric value; or a future library version adds a new enum member that an older binary does not handle.

Related errors


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