{"record":{"id":"6b07633b6be33121","repo":"microsoft/aspire","slug":"toolbox-name-changed-concurrently-default-version-6b0763","errorCode":null,"errorMessage":"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.","messagePattern":"Toolbox '(.+?)' changed concurrently: default version '(.+?)' matched, but version '(.+?)' with a different configuration is now the default\\. No further changes were made\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Aspire.Hosting.Foundry/Toolbox/FoundryToolboxReconciler.cs","lineNumber":402,"sourceCode":"\n    private async Task VerifyReusedDefaultAsync(\n        FoundryToolboxDeploymentDefinition definition,\n        string expectedDefaultVersion,\n        CancellationToken cancellationToken)\n    {\n        var current = await administration.GetAsync(definition.Name, cancellationToken).ConfigureAwait(false)\n            ?? throw CreateConcurrentChangeException(\n                definition.Name,\n                $\"default version '{expectedDefaultVersion}' matched, but the Toolbox is no longer visible\");\n\n        ValidateOwnedDefault(definition.Name, current, concurrentChange: true);\n        if (!string.Equals(current.DefaultVersion, expectedDefaultVersion, StringComparison.Ordinal) ||\n            !current.Default.Metadata.TryGetValue(\n                FoundryToolboxDeploymentDefinition.ConfigurationHashMetadataKey,\n                out var currentHash) ||\n            !string.Equals(currentHash, definition.ConfigurationHash, StringComparison.Ordinal))\n        {\n            throw CreateConcurrentChangeException(\n                definition.Name,\n                $\"default version '{expectedDefaultVersion}' matched, but version '{current.DefaultVersion}' with a different configuration is now the default\");\n        }\n    }\n\n    private async Task PromoteOwnedVersionAsync(\n        FoundryToolboxDeploymentDefinition definition,\n        string version,\n        string? observedDefaultVersion,\n        CancellationToken cancellationToken)\n    {\n        // Toolbox administration has no conditional update or ETag support. Re-read immediately\n        // before promotion and require the exact default observed while reconciling. Otherwise one\n        // Aspire deployment could overwrite another Aspire deployment's newer default. This is\n        // coordination, not an atomic lock: another writer can still update the Toolbox after the\n        // final read.\n        var before = await administration.GetAsync(definition.Name, cancellationToken).ConfigureAwait(false)\n            ?? throw CreateConcurrentChangeException(","sourceCodeStart":384,"sourceCodeEnd":420,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Hosting.Foundry/Toolbox/FoundryToolboxReconciler.cs#L384-L420","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Re-run the deployment so the reconciler observes and promotes the intended version freshly.","Serialize deployments to the same Foundry project (avoid concurrent envs/branches).","Align Toolbox configuration across pipelines, or give each pipeline its own Toolbox name/project.","Compare the reported current.DefaultVersion with the expected version to identify which deployment won."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try\n{\n    await deploy.RunAsync();\n}\ncatch (Exception ex) when (ex.Message.Contains(\"with a different configuration is now the default\"))\n{\n    logger.LogWarning(ex, \"Another deployment changed the Toolbox default; retrying.\");\n    await deploy.RunAsync();\n}","preventionTips":["Ensure all pipelines deploy identical Toolbox configuration, or isolate per-environment Toolboxes.","Serialize deployments to the same Foundry project.","Don't promote default versions manually in the portal during deploys."],"tags":["azure","foundry","toolbox","concurrency","deployment"],"backgroundTag":"concurrent-modification","analyzedSha":"25830f84bd145686607ad00c057b3f84e2e51d43","analyzedAt":"2026-09-16T11:10:06.193Z","contentChangedAt":"2026-09-16T11:10:06.193Z","schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}