microsoft/aspire · error · InvalidOperationException

Toolbox ' ' changed concurrently: default version ' '…

Error message

Toolbox '{name}' changed concurrently: default version '{expectedDefaultVersion}' matched, but version '{current.DefaultVersion}' with a different configuration is now the default. No further changes were made.

What it means

The Foundry Toolbox reconciler verifies, immediately before reusing an existing default, that the default version and its configuration hash are unchanged since reconciliation (Foundry Toolbox administration lacks conditional updates). This error means another writer switched the Toolbox default to a different version with different configuration between the reconciler's read and its action; the reconciler aborts without changes to avoid overwriting another deployment's newer default.

Solutions

  1. Re-run the deployment so the reconciler observes and promotes the intended version freshly.
  2. Serialize deployments to the same Foundry project (avoid concurrent envs/branches).
  3. Align Toolbox configuration across pipelines, or give each pipeline its own Toolbox name/project.
  4. Compare the reported current.DefaultVersion with the expected version to identify which deployment won.
Defensive patterns

Strategy: retry

Try / catch

try
{
    await deploy.RunAsync();
}
catch (Exception ex) when (ex.Message.Contains("with a different configuration is now the default"))
{
    logger.LogWarning(ex, "Another deployment changed the Toolbox default; retrying.");
    await deploy.RunAsync();
}

Prevention

When it happens

Trigger: VerifyReusedDefaultAsync finds current.DefaultVersion != expectedDefaultVersion (ordinal compare), or current.Default.Metadata lacks ConfigurationHashMetadataKey or its hash differs from definition.ConfigurationHash; ReconcileAsync throws the concurrent-change error.

Common situations: Two pipelines deploying different Toolbox configurations to the same project; manual default-version promotion in the portal during deploy; a stale deployment re-run against a Toolbox already updated by a newer deployment.

Related errors


AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16). Data as JSON: /api/errors/6b07633b6be33121. Report an issue: GitHub.

Appendix: source

Thrown at src/Aspire.Hosting.Foundry/Toolbox/FoundryToolboxReconciler.cs:402

    private async Task VerifyReusedDefaultAsync(
        FoundryToolboxDeploymentDefinition definition,
        string expectedDefaultVersion,
        CancellationToken cancellationToken)
    {
        var current = await administration.GetAsync(definition.Name, cancellationToken).ConfigureAwait(false)
            ?? throw CreateConcurrentChangeException(
                definition.Name,
                $"default version '{expectedDefaultVersion}' matched, but the Toolbox is no longer visible");

        ValidateOwnedDefault(definition.Name, current, concurrentChange: true);
        if (!string.Equals(current.DefaultVersion, expectedDefaultVersion, StringComparison.Ordinal) ||
            !current.Default.Metadata.TryGetValue(
                FoundryToolboxDeploymentDefinition.ConfigurationHashMetadataKey,
                out var currentHash) ||
            !string.Equals(currentHash, definition.ConfigurationHash, StringComparison.Ordinal))
        {
            throw CreateConcurrentChangeException(
                definition.Name,
                $"default version '{expectedDefaultVersion}' matched, but version '{current.DefaultVersion}' with a different configuration is now the default");
        }
    }

    private async Task PromoteOwnedVersionAsync(
        FoundryToolboxDeploymentDefinition definition,
        string version,
        string? observedDefaultVersion,
        CancellationToken cancellationToken)
    {
        // Toolbox administration has no conditional update or ETag support. Re-read immediately
        // before promotion and require the exact default observed while reconciling. Otherwise one
        // Aspire deployment could overwrite another Aspire deployment's newer default. This is
        // coordination, not an atomic lock: another writer can still update the Toolbox after the
        // final read.
        var before = await administration.GetAsync(definition.Name, cancellationToken).ConfigureAwait(false)
            ?? throw CreateConcurrentChangeException(

View on GitHub (pinned to 25830f84bd)