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
- Validate the CompactionType with Enum.IsDefined before calling Compact and reject/normalize unknown values at the trust boundary.
- Pass only CompactionType.Scan or CompactionType.Lookup from strongly-typed code; never cast an integer to CompactionType.
- 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
- Never cast arbitrary integers to CompactionType; map config strings to known members explicitly.
- Validate enums from config/deserialization with Enum.IsDefined at the boundary.
- Pass only CompactionType.Scan or CompactionType.Lookup from strongly-typed code.
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
- Value cannot be null. (Parameter 'functions')
- InPlaceWriter(ReadOnlySpan<byte> value) is not supported in
- InPlaceWriter(IHeapObject value) is not supported in this IS
- GetRMWModifiedFieldInfo is not supported in this ISessionFun
- GetRMWInitialFieldInfo is not supported in this ISessionFunc
AI-assisted analysis of microsoft/garnet@951b0fc683 (2026-08-13).
Data as JSON: /api/errors/7761c6ac669d0a6f.
Report an issue: GitHub.