microsoft/garnet · error · TsavoriteException
LogSettings.LogDevice needs to be specified (e.g., use Devic
Error message
LogSettings.LogDevice needs to be specified (e.g., use Devices.CreateLogDevice, AzureStorageDevice, or NullDevice)
What it means
Constructor validation: logSettings.LogDevice must be non-null. The allocator persists and recovers via this IDevice; without one there is nowhere to read/write the log. The message suggests concrete factories: Devices.CreateLogDevice, AzureStorageDevice, or NullDevice (the last for in-memory-only / ephemeral use).
Source
Thrown at libs/storage/Tsavorite/cs/src/core/Allocator/AllocatorBase.cs:587
throw new TsavoriteException($"{nameof(rcs.PageSizeBits)} must be between {LogSettings.kMinPageSizeBits} and {LogSettings.kMaxPageSizeBits}");
if (rcs.PageCount > MemoryUtils.ArrayMaxLength)
throw new TsavoriteException($"{nameof(rcs.PageCount)} must be less than or equal to the maximum array length ({MemoryUtils.ArrayMaxLength})");
if (rcs.MemorySize != 0 && (rcs.MemorySize < 1L << LogSettings.kMinMemorySizeBits || rcs.MemorySize > 1L << LogSettings.kMaxMemorySizeBits))
throw new TsavoriteException($"{nameof(rcs.MemorySize)} must be between {1L << LogSettings.kMinMemorySizeBits} and {1L << LogSettings.kMaxMemorySizeBits}");
if ((rcs.MemorySize != 0) && (rcs.MemorySize < (1L << rcs.PageSizeBits) * 2))
throw new TsavoriteException($"{nameof(logSettings.MemorySize)} must be at least twice the page size ({1L << rcs.PageSizeBits})");
if (rcs.SecondChanceFraction < 0.0 || rcs.SecondChanceFraction > 1.0)
throw new TsavoriteException($"{rcs.SecondChanceFraction} must be >= 0.0 and <= 1.0");
}
if (logSettings.MaxInlineKeySize < LogSettings.MinMaxInlineSize || logSettings.MaxInlineKeySize > LogSettings.MaxInlineKeySizeLimit)
throw new TsavoriteException($"{nameof(logSettings.MaxInlineKeySize)} must be between {LogSettings.MinMaxInlineSize} and {LogSettings.MaxInlineKeySizeLimit}");
if (logSettings.MaxInlineValueSize < LogSettings.MinMaxInlineSize || logSettings.MaxInlineValueSize > LogSettings.MaxInlineValueSizeLimit)
throw new TsavoriteException($"{nameof(logSettings.MaxInlineValueSize)} must be between {LogSettings.MinMaxInlineSize} and {LogSettings.MaxInlineValueSizeLimit}");
this.logger = logger;
if (logSettings.LogDevice == null)
throw new TsavoriteException("LogSettings.LogDevice needs to be specified (e.g., use Devices.CreateLogDevice, AzureStorageDevice, or NullDevice)");
EvictCallback = evictCallback;
FlushCallback = flushCallback;
PreallocateLog = logSettings.PreallocateLog;
flushEvent.Initialize();
IsNullDevice = logSettings.LogDevice is NullDevice;
if (epoch == null)
{
this.epoch = new LightEpoch();
isEpochOwned = true;
}
else
this.epoch = epoch;
logSettings.LogDevice.Initialize(1L << logSettings.SegmentSizeBits, epoch);View on GitHub (pinned to 951b0fc683)
Solutions
- Assign a real device: `LogDevice = Devices.CreateLogDevice(path)`.
- For ephemeral/in-memory stores use `LogDevice = new NullDevice()`.
- For Azure-backed storage use AzureStorageDevice as the message suggests.
Example fix
// before
var s = new LogSettings { PageSizeBits = 25 /* LogDevice never set */ };
// after
var s = new LogSettings { LogDevice = Devices.CreateLogDevice("./data"), PageSizeBits = 25 };
// or, in-memory only
var s = new LogSettings { LogDevice = new NullDevice() }; Defensive patterns
Strategy: type-guard
Validate before calling
if (logSettings.LogDevice is null)
throw new ArgumentException("LogSettings.LogDevice is null. Use Devices.CreateLogDevice(path), AzureStorageDevice, or new NullDevice()."); Type guard
static bool HasLogDevice(LogSettings s) => s?.LogDevice is not null; // usage if (!HasLogDevice(logSettings)) logSettings.LogDevice = Devices.CreateLogDevice(DefaultPath);
Try / catch
try { new TsavoriteKV<K,V>(logSettings, ...); }
catch (TsavoriteException ex) when (ex.Message.Contains("LogDevice needs to be specified"))
{ /* assign logSettings.LogDevice (CreateLogDevice/AzureStorageDevice/NullDevice) and recreate */ } Prevention
- Always assign LogDevice in the same object initializer that builds LogSettings.
- Use NullDevice explicitly for in-memory tests rather than relying on a null default.
- Centralize device creation in one factory so LogDevice is never left null.
When it happens
Trigger: Constructing TsavoriteKV/TsavoriteLog with `LogSettings.LogDevice = null` (e.g. forgetting to assign it, or a factory call that returned null on a bad path).
Common situations: Building LogSettings from a config object whose LogDevice field was never populated; conditional device creation where the branch returned null; tests that intended NullDevice but assigned nothing.
Related errors
- Allocator with sector size {sectorSize} cannot flush to devi
- {nameof(logSettings.PageCount)} or {nameof(logSettings.Memor
- {nameof(logSettings.PageSizeBits)} must be between {LogSetti
- {nameof(logSettings.PageSizeBits)} must be >= PageHeader.Siz
- {nameof(logSettings.PageCount)} must be less than or equal t
AI-assisted analysis of microsoft/garnet@951b0fc683 (2026-08-13).
Data as JSON: /api/errors/cb70681615e6a354.
Report an issue: GitHub.