microsoft/FASTER · error · InvalidOperationException
Recovery of blobs is single-threaded and should not yield…
Error message
Recovery of blobs is single-threaded and should not yield any failure due to concurrency
What it means
InvalidOperationException thrown during AzureStorageDevice.StartAsync recovery when TryAdd of a discovered page-blob into the segment map returns false, meaning the same segmentId was already registered. Recovery enumerates blobs in the container single-threadedly, so a duplicate indicates two blobs mapping to the same segment id — an unexpected storage-state corruption.
Solutions
- Inspect the storage container for duplicate/conflicting segment blob names and remove or rename the stale ones
- Use a dedicated container per FASTER device instance; do not share containers across devices or concurrent runs
- Delete the container (or blobs) and reinitialize the device if leftover state is disposable
- Check whether a previous run left orphaned blobs from a crashed checkpoint and clean up before recovery
Example fix
// before
var device = new AzureStorageDevice("accountKey", "account", "shared-container", "dir", ...); // container shared with old runs
// after
var device = new AzureStorageDevice("accountKey", "account", "device-42-dedicated-container", "dir", ...); // unique container per device Defensive patterns
Strategy: try-catch
Validate before calling
// before constructing the device, list blobs and detect duplicate segment ids
var names = await container.GetBlobsAsync();
var dupes = names.GroupBy(b => ParseSegmentId(b.Name)).Where(g => g.Count() > 1).ToList();
if (dupes.Any()) throw new InvalidOperationException("Duplicate segment blobs: " + dupes.First().Key); Try / catch
try { await device.StartAsync(...); }
catch (InvalidOperationException ex) when (ex.Message.Contains("Recovery of blobs is single-threaded"))
{ logger.Error("Duplicate segment blobs in container; clean up and retry", ex); } Prevention
- Use one dedicated Azure container per FASTER device instance
- Clean orphaned/duplicated blobs before initializing a device on existing storage
- Avoid manual blob copies/restores into an active device container
When it happens
Trigger: Running StartAsync (constructor path of AzureStorageDevice) against a container containing blobs whose names map to the same parsed segmentId, or leftover duplicated/conflicting blob names from a prior misconfigured or crashed session.
Common situations: Reusing an Azure container across multiple FASTER devices/instances; manually copied or restored blobs with name collisions; leftover blobs after deleting and recreating a device on the same container; concurrent instance misuse assumptions.
Understand the failure class
Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.
Related errors
- Failed to open file for hybrid log
- Allocator with sector size
- Unexpected entry type
- Unable to set first valid segment to
- Unable to set last valid segment to
AI-assisted analysis of microsoft/FASTER@321d872eab (2026-09-15).
Data as JSON: /api/errors/f53386cf90cd47ff.
Report an issue: GitHub.
Appendix: source
Thrown at cs/src/devices/AzureStorageDevice/AzureStorageDevice.cs:204
.AsPages(continuationToken, 100)
.FirstAsync();
pageResults = page.Values;
continuationToken = page.ContinuationToken;
return page.Values.Count; // not accurate, in terms of bytes, but still useful for tracing purposes
});
foreach (var item in pageResults)
{
if (Int32.TryParse(item.Name.Replace(prefix, ""), out int segmentId))
{
this.BlobManager?.StorageTracer?.FasterStorageProgress($"AzureStorageDevice.StartAsync found segment={item.Name}");
bool ret = this.blobs.TryAdd(segmentId, new BlobEntry(BlobUtilsV12.GetPageBlobClients(this.pageBlobDirectory.Client, item.Name), item.Properties.ETag.Value, this));
if (!ret)
{
throw new InvalidOperationException("Recovery of blobs is single-threaded and should not yield any failure due to concurrency");
}
}
}
}
while (!string.IsNullOrEmpty(continuationToken));
// make sure we did not lose the lease while iterating to find the blobs
await this.BlobManager.ConfirmLeaseIsGoodForAWhileAsync();
this.StorageErrorHandler.Token.ThrowIfCancellationRequested();
// find longest contiguous sequence at end
var keys = this.blobs.Keys.ToList();
if (keys.Count == 0)
{
// nothing has been written to this device so far.
this.startSegment = 0;
this.endSegment = -1;View on GitHub (pinned to 321d872eab)