microsoft/FASTER · error · InvalidOperationException
AsyncReadRecordObjectsToMemory invalid for…
Error message
AsyncReadRecordObjectsToMemory invalid for BlittableAllocator
What it means
AsyncReadRecordObjectsToMemory exists to hydrate object references from the object log; BlittableAllocator stores fixed-size binary records and has no per-record objects, so it deliberately does not implement this operation and throws InvalidOperationException. Calling it is always a caller mistake - the wrong allocator path was used.
Solutions
- Use the normal ReadAsync API; for blittable allocators records are returned directly without object hydration.
- If you need object-log semantics, configure Key/Value as classes so the ObjectAllocator (not BlittableAllocator) is used.
- Remove/branch code paths that call the objects-to-memory read variant when IDevice/log is blittable.
Example fix
// before // blittable store (struct Key/Value) store.Log.AsyncReadRecordObjectsToMemory(addr, len, callback, ctx, result); // after var (status, output) = (await session.ReadAsync(key)).Complete(); // standard read path for blittable records
Defensive patterns
Strategy: validation
Validate before calling
if (typeof(Key).IsValueType || typeof(Value).IsValueType)
throw new InvalidOperationException("Blittable store: use ReadAsync, not object-hydration reads"); Type guard
bool IsObjectAllocator<FasterLog>(FasterKV<K, V> store) => typeof(K).IsClass || typeof(V).IsClass; // object-hydration APIs only valid when types are classes
Try / catch
try { /* object read path */ }
catch (InvalidOperationException ex) when (ex.Message.Contains("invalid for BlittableAllocator")) { /* switch to standard ReadAsync */ } Prevention
- Use the public ReadAsync API; avoid internal objects-to-memory paths unless using the ObjectAllocator.
- Keep read code paths aligned with whether Key/Value are classes or structs.
- Avoid reflection-driven generic read helpers that assume object logs.
When it happens
Trigger: Invoking ReadAsync with the objects path / internal ReadRecordObjects variant on a FasterKV configured with blittable (non-ICanSerialize/non-object) Key/Value types.
Common situations: Generic or reflection-based helper code calling AsyncReadRecordObjectsToMemory regardless of allocator type; switching Key/Value from class to struct types (object to blittable) without updating read logic.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- BlittableAllocator memory pages are sector aligned - use…
- Pending reads not supported with pub/sub
- Cannot use BlittableParameterSerializer with non-blittable…
- Pending reads not supported with pub/sub
- Out of order message within session
AI-assisted analysis of microsoft/FASTER@321d872eab (2026-09-15).
Data as JSON: /api/errors/df559233cc29eeb4.
Report an issue: GitHub.
Appendix: source
Thrown at cs/src/core/Allocator/BlittableAllocator.cs:328
ulong alignedSourceAddress, int destinationPageIndex, uint aligned_read_length,
DeviceIOCompletionCallback callback, PageAsyncReadResult<TContext> asyncResult, IDevice device, IDevice objlogDevice)
{
device.ReadAsync(alignedSourceAddress, (IntPtr)pointers[destinationPageIndex],
aligned_read_length, callback, asyncResult);
}
/// <summary>
/// Invoked by users to obtain a record from disk. It uses sector aligned memory to read
/// the record efficiently into memory.
/// </summary>
/// <param name="fromLogical"></param>
/// <param name="numBytes"></param>
/// <param name="callback"></param>
/// <param name="context"></param>
/// <param name="result"></param>
protected override void AsyncReadRecordObjectsToMemory(long fromLogical, int numBytes, DeviceIOCompletionCallback callback, AsyncIOContext<Key, Value> context, SectorAlignedMemory result = default)
{
throw new InvalidOperationException("AsyncReadRecordObjectsToMemory invalid for BlittableAllocator");
}
/// <summary>
/// Retrieve objects from object log
/// </summary>
/// <param name="record"></param>
/// <param name="ctx"></param>
/// <returns></returns>
protected override bool RetrievedFullRecord(byte* record, ref AsyncIOContext<Key, Value> ctx)
{
ctx.key = GetKey((long)record);
ctx.value = GetValue((long)record);
return true;
}
/// <summary>
/// Whether KVS has keys to serialize/deserialize
/// </summary>View on GitHub (pinned to 321d872eab)