microsoft/garnet · error · TsavoriteException
Allocator with sector size {sectorSize} cannot flush to devi
Error message
Allocator with sector size {sectorSize} cannot flush to device with sector size {device.SectorSize} What it means
Thrown by AllocatorBase.VerifyCompatibleSectorSize(IDevice) before flushing pages to a storage device. The allocator performs sector-aligned I/O using the sector size it captured from its primary LogDevice at construction; if it is asked to flush to a different device whose SectorSize does not evenly divide that captured value, alignment would be violated and on-disk data could be corrupted, so the write is rejected up front. The check is `sectorSize % device.SectorSize != 0`, where `sectorSize` is the allocator's own (inherited from the original LogDevice).
Source
Thrown at libs/storage/Tsavorite/cs/src/core/Allocator/AllocatorBase.cs:489
if (isEpochOwned)
epoch.Dispose();
bufferPool.Free();
flushEvent.Dispose();
notifyFlushedUntilAddressTcs?.TrySetCanceled();
notifyFlushedUntilAddressTcs = null;
onReadOnlyObserver?.OnCompleted();
onEvictionObserver?.OnCompleted();
}
#endregion abstract and virtual methods
private protected void VerifyCompatibleSectorSize(IDevice device)
{
if (sectorSize % device.SectorSize != 0)
throw new TsavoriteException($"Allocator with sector size {sectorSize} cannot flush to device with sector size {device.SectorSize}");
}
/// <summary>
/// This writes data from a page (or pages) for allocators that support only inline data.
/// </summary>
/// <param name="alignedSourceAddress">The source address, aligned to start of allocator page</param>
/// <param name="alignedDestinationAddress">The destination address, aligned to start of allocator page</param>
/// <param name="numBytesToWrite">Number of bytes to be written, based on allocator page range</param>
/// <param name="callback">The callback for the operation</param>
/// <param name="asyncResult">The callback state information, including information for the flush operation</param>
/// <param name="device">The device to write to</param>
[MethodImpl(MethodImplOptions.NoInlining)]
internal void WriteInlinePageAsync<TContext>(IntPtr alignedSourceAddress, ulong alignedDestinationAddress, uint numBytesToWrite,
DeviceIOCompletionCallback callback, PageAsyncFlushResult<TContext> asyncResult, IDevice device)
{
if (asyncResult.partial)
{
// Write only required bytes within the pageView on GitHub (pinned to 951b0fc683)
Solutions
- Make the target device's SectorSize a divisor of the allocator's sectorSize. Easiest: ensure the primary LogDevice and any device the same allocator flushes to report the same SectorSize.
- If you control the custom IDevice, set its SectorSize to a value that divides the main device's (e.g. 512 when the main device is 512 or 4096), or match it exactly.
- Recreate the Tsavorite store so the allocator captures the desired sectorSize from a device consistent with every device it will flush to.
- For checkpoint-to-disk flows, point checkpoints at a device constructed with the same sector size as the log device (Devices.CreateLogDevice uses the same sector-size logic for both).
Example fix
// before: main log on 512-byte device, checkpoint to a 4096-byte device
var logDev = Devices.CreateLogDevice("log", sectorSize: 512);
var ckptDev = new MyCustomDevice(sectorSize: 4096);
// -> VerifyCompatibleSectorSize throws during flush
// after: align sector sizes
var logDev = Devices.CreateLogDevice("log", sectorSize: 512);
var ckptDev = new MyCustomDevice(sectorSize: 512); // divides/matches allocator sectorSize Defensive patterns
Strategy: validation
Validate before calling
// Before flushing the allocator to a secondary device, confirm sector compatibility.
// 'allocatorSectorSize' is the sector size the allocator captured from its primary LogDevice
// (expose it via your wrapper, or read device.SectorSize of the original log device).
static bool CanFlushToDevice(int allocatorSectorSize, IDevice target)
=> target is not null && allocatorSectorSize % target.SectorSize == 0;
// usage
if (!CanFlushToDevice(logDevice.SectorSize, checkpointDevice))
throw new InvalidOperationException(
$"Cannot flush: allocator sector {logDevice.SectorSize} not divisible by device sector {checkpointDevice.SectorSize}"); Try / catch
try
{
await store.FlushAsync(); // or checkpoint to the secondary device
}
catch (TsavoriteException ex) when (ex.Message.Contains("cannot flush to device with sector size"))
{
// Sector-size mismatch: reconcile device SectorSize values, then retry on a rebuilt store.
logger.Error("Sector-size mismatch on flush; rebuild store with matching device sector sizes: {Msg}", ex.Message);
throw;
} Prevention
- Construct every device the store will flush to with the same SectorSize as the primary LogDevice.
- For custom IDevice implementations, return a SectorSize that divides the main device's SectorSize.
- Centralize device creation in one factory so sector sizes cannot drift.
When it happens
Trigger: A flush or checkpoint path routes data to an IDevice whose SectorSize is not a divisor of the allocator's sectorSize. For example the main hybrid log was opened on a device with sectorSize 512, but the operation flushes to a device (or a custom IDevice wrapper, or a copy-to device) reporting SectorSize 4096; 512 % 4096 != 0, so the guard fires.
Common situations: Mixing backing stores in one store (local SSD for the log, Azure/network device for checkpoints or a secondary copy); wrapping a real device in a custom IDevice whose SectorSize getter returns an unrelated power of two; upgrading a device layer that changed its reported SectorSize (e.g. a 512-native device now reporting 4096 logical sectors) without re-creating the allocator.
Related errors
- LogSettings.LogDevice needs to be specified (e.g., use Devic
- Page size must be at least of device sector size ({sectorSiz
- Unable to open ACL configuration file '{aclConfigurationFile
- {nameof(logSettings.PageCount)} or {nameof(logSettings.Memor
- {nameof(logSettings.PageSizeBits)} must be between {LogSetti
AI-assisted analysis of microsoft/garnet@951b0fc683 (2026-08-13).
Data as JSON: /api/errors/f5bb881102c3ec79.
Report an issue: GitHub.