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

  1. Retry the settings update after a short delay once competing writes finish.
  2. Serialize your own updates so only one settings write runs at a time in your process.
  3. Increase the write-lock timeout if writes are legitimately slow (adjust WriteLockTimeoutMs or the hosting code).
  4. Check for a stuck Aspire/host process still holding the semaphore and terminate it.
  5. 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

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


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)