{"record":{"id":"cb70681615e6a354","repo":"microsoft/garnet","slug":"logsettings-logdevice-needs-to-be-specified-e-g","errorCode":null,"errorMessage":"LogSettings.LogDevice needs to be specified (e.g., use Devices.CreateLogDevice, AzureStorageDevice, or NullDevice)","messagePattern":"LogSettings\\.LogDevice needs to be specified \\(e\\.g\\., use Devices\\.CreateLogDevice, AzureStorageDevice, or NullDevice\\)","errorType":"validation","errorClass":"TsavoriteException","httpStatus":null,"severity":"error","filePath":"libs/storage/Tsavorite/cs/src/core/Allocator/AllocatorBase.cs","lineNumber":587,"sourceCode":"                    throw new TsavoriteException($\"{nameof(rcs.PageSizeBits)} must be between {LogSettings.kMinPageSizeBits} and {LogSettings.kMaxPageSizeBits}\");\n                if (rcs.PageCount > MemoryUtils.ArrayMaxLength)\n                    throw new TsavoriteException($\"{nameof(rcs.PageCount)} must be less than or equal to the maximum array length ({MemoryUtils.ArrayMaxLength})\");\n                if (rcs.MemorySize != 0 && (rcs.MemorySize < 1L << LogSettings.kMinMemorySizeBits || rcs.MemorySize > 1L << LogSettings.kMaxMemorySizeBits))\n                    throw new TsavoriteException($\"{nameof(rcs.MemorySize)} must be between {1L << LogSettings.kMinMemorySizeBits} and {1L << LogSettings.kMaxMemorySizeBits}\");\n                if ((rcs.MemorySize != 0) && (rcs.MemorySize < (1L << rcs.PageSizeBits) * 2))\n                    throw new TsavoriteException($\"{nameof(logSettings.MemorySize)} must be at least twice the page size ({1L << rcs.PageSizeBits})\");\n                if (rcs.SecondChanceFraction < 0.0 || rcs.SecondChanceFraction > 1.0)\n                    throw new TsavoriteException($\"{rcs.SecondChanceFraction} must be >= 0.0 and <= 1.0\");\n            }\n\n            if (logSettings.MaxInlineKeySize < LogSettings.MinMaxInlineSize || logSettings.MaxInlineKeySize > LogSettings.MaxInlineKeySizeLimit)\n                throw new TsavoriteException($\"{nameof(logSettings.MaxInlineKeySize)} must be between {LogSettings.MinMaxInlineSize} and {LogSettings.MaxInlineKeySizeLimit}\");\n            if (logSettings.MaxInlineValueSize < LogSettings.MinMaxInlineSize || logSettings.MaxInlineValueSize > LogSettings.MaxInlineValueSizeLimit)\n                throw new TsavoriteException($\"{nameof(logSettings.MaxInlineValueSize)} must be between {LogSettings.MinMaxInlineSize} and {LogSettings.MaxInlineValueSizeLimit}\");\n\n            this.logger = logger;\n            if (logSettings.LogDevice == null)\n                throw new TsavoriteException(\"LogSettings.LogDevice needs to be specified (e.g., use Devices.CreateLogDevice, AzureStorageDevice, or NullDevice)\");\n\n            EvictCallback = evictCallback;\n\n            FlushCallback = flushCallback;\n            PreallocateLog = logSettings.PreallocateLog;\n            flushEvent.Initialize();\n\n            IsNullDevice = logSettings.LogDevice is NullDevice;\n\n            if (epoch == null)\n            {\n                this.epoch = new LightEpoch();\n                isEpochOwned = true;\n            }\n            else\n                this.epoch = epoch;\n\n            logSettings.LogDevice.Initialize(1L << logSettings.SegmentSizeBits, epoch);","sourceCodeStart":569,"sourceCodeEnd":605,"githubUrl":"https://github.com/microsoft/garnet/blob/951b0fc6838721f89d102c2bbe1b914e8d39d700/libs/storage/Tsavorite/cs/src/core/Allocator/AllocatorBase.cs#L569-L605","documentation":"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).","triggerScenarios":"Constructing TsavoriteKV/TsavoriteLog with `LogSettings.LogDevice = null` (e.g. forgetting to assign it, or a factory call that returned null on a bad path).","commonSituations":"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.","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."],"exampleFix":"// before\nvar s = new LogSettings { PageSizeBits = 25 /* LogDevice never set */ };\n// after\nvar s = new LogSettings { LogDevice = Devices.CreateLogDevice(\"./data\"), PageSizeBits = 25 };\n// or, in-memory only\nvar s = new LogSettings { LogDevice = new NullDevice() };","handlingStrategy":"type-guard","validationCode":"if (logSettings.LogDevice is null)\n    throw new ArgumentException(\"LogSettings.LogDevice is null. Use Devices.CreateLogDevice(path), AzureStorageDevice, or new NullDevice().\");","typeGuard":"static bool HasLogDevice(LogSettings s) => s?.LogDevice is not null;\n\n// usage\nif (!HasLogDevice(logSettings)) logSettings.LogDevice = Devices.CreateLogDevice(DefaultPath);","tryCatchPattern":"try { new TsavoriteKV<K,V>(logSettings, ...); }\ncatch (TsavoriteException ex) when (ex.Message.Contains(\"LogDevice needs to be specified\"))\n{ /* assign logSettings.LogDevice (CreateLogDevice/AzureStorageDevice/NullDevice) and recreate */ }","preventionTips":["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."],"tags":["storage","configuration","log-settings","device","null-reference"],"backgroundTag":null,"analyzedSha":"951b0fc6838721f89d102c2bbe1b914e8d39d700","analyzedAt":"2026-08-13T19:01:32.939Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}