microsoft/aspire · error · DistributedApplicationException
Failed to acquire semaphore for settings file
Error message
Failed to acquire semaphore for settings file: {settingsPath} What it means
DevcontainerSettingsWriter serializes concurrent writes to devcontainer settings files with a semaphore that has a timeout (WriteLockTimeoutMs). If the lock cannot be acquired within that time while processing a port update, it throws DistributedApplicationException naming the settings file it failed to lock.
Solutions
- Retry the settings update after a short delay once competing writes finish.
- Serialize your own updates so only one settings write runs at a time in your process.
- Increase the write-lock timeout if writes are legitimately slow (adjust WriteLockTimeoutMs or the hosting code).
- Check for a stuck Aspire/host process still holding the semaphore and terminate it.
- Verify the settings file location is on local disk, not a slow network mount.
Example fix
// before
await settingsWriter.WriteSettingsAsync(...); // races with another writer -> lock timeout
// after
await _updateGate.WaitAsync(); // app-level serialization
try { await settingsWriter.WriteSettingsAsync(...); }
finally { _updateGate.Release(); } Defensive patterns
Strategy: retry
Validate before calling
// detect concurrent writers before updating
if (Interlocked.CompareExchange(ref updating, 1, 1) == 1) Console.WriteLine("A settings update is already in progress"); Try / catch
const int MaxRetries = 3;
for (var attempt = 1; ; attempt++)
{
try { await settingsWriter.WriteSettingsAsync(...); break; }
catch (DistributedApplicationException ex) when (ex.Message.StartsWith("Failed to acquire semaphore") && attempt < MaxRetries)
{ await Task.Delay(TimeSpan.FromSeconds(2 * attempt)); }
} Prevention
- Serialize devcontainer settings updates in your tooling
- Avoid writing settings from multiple processes concurrently
- Keep settings paths on local disk
- Retry transient lock failures with backoff
When it happens
Trigger: WriteSettingsAsync/ProcessPortUpdatesAsync called while another writer holds the lock longer than WriteLockTimeoutMs — e.g. overlapping port-update operations or a hung prior write to the same settings path.
Common situations: Multiple Aspire operations racing to update the devcontainer settings, a very slow disk or network-mounted home directory, another process/tool holding the settings file busy causing long writes, or a leaked lock from a cancelled but still-running task.
Understand the failure class
Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.
Related errors
- Failed to acquire file lock
- Failed to acquire file lock
- Azure operation ' ' is already running or queued for the…
- Build for resource ' ' timed out after .
- Callback ' ' timed out after s
AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16).
Data as JSON: /api/errors/11aaebd9ebb775cd.
Report an issue: GitHub.
Appendix: source
Thrown at src/Aspire.Hosting/Devcontainers/DevcontainerSettingsWriter.cs:114
catch (OperationCanceledException) when (_processingCancellation.IsCancellationRequested)
{
// Normal shutdown.
}
}
private async Task WriteSettingsAsync(IReadOnlyList<PortForwardEntry> newPorts, CancellationToken cancellationToken)
{
var settingsPaths = GetSettingsPaths();
// Collect ports we actually wrote so we can log them AFTER the file save completes.
List<(string Label, string Url)> portsToLog = [];
foreach (var settingsPath in settingsPaths)
{
var acquired = await _writeLock.WaitAsync(WriteLockTimeoutMs, cancellationToken).ConfigureAwait(false);
if (!acquired)
{
throw new DistributedApplicationException($"Failed to acquire semaphore for settings file: {settingsPath}");
}
await EnsureSettingsFileExists(settingsPath, cancellationToken).ConfigureAwait(false);
var settingsContent = await File.ReadAllTextAsync(settingsPath, cancellationToken).ConfigureAwait(false);
var settings = (JsonObject)JsonObject.Parse(settingsContent)!;
JsonObject? portsAttributes;
if (!settings.TryGetPropertyValue(PortAttributesFieldName, out var portsAttributesNode))
{
portsAttributes = [];
settings.Add(PortAttributesFieldName, portsAttributes);
}
else
{
portsAttributes = (JsonObject)portsAttributesNode!;
}
View on GitHub (pinned to 25830f84bd)