{"record":{"id":"fd3964af5ae38665","repo":"microsoft/aspire","slug":"toolbox-name-changed-concurrently-default-version-state","errorCode":null,"errorMessage":"Toolbox '{name}' changed concurrently: default version '{state.DefaultVersion}' is not managed by Aspire. No further changes were made.","messagePattern":"Toolbox '(.+?)' changed concurrently: default version '(.+?)' is not managed by Aspire\\. No further changes were made\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Aspire.Hosting.Foundry/Toolbox/FoundryToolboxReconciler.cs","lineNumber":474,"sourceCode":"            throw CreateConcurrentChangeException(\n                definition.Name,\n                $\"version '{version}' was promoted, but version '{after.DefaultVersion}' is now the default\");\n        }\n    }\n\n    private static void ValidateOwnedDefault(\n        string name,\n        FoundryToolboxState state,\n        bool concurrentChange = false)\n    {\n        if (IsAspireManaged(state.Default))\n        {\n            return;\n        }\n\n        if (concurrentChange)\n        {\n            throw CreateConcurrentChangeException(\n                name,\n                $\"default version '{state.DefaultVersion}' is not managed by Aspire\");\n        }\n\n        throw new InvalidOperationException(\n            $\"Toolbox '{name}' already exists but is not managed by Aspire. No changes were made.\");\n    }\n\n    private static void ValidatePromotionTarget(\n        FoundryToolboxDeploymentDefinition definition,\n        FoundryToolboxState state,\n        string version)\n    {\n        var target = state.Versions.FirstOrDefault(candidate =>\n            string.Equals(candidate.Version, version, StringComparison.Ordinal));\n        if (target is null)\n        {\n            throw CreateConcurrentChangeException(","sourceCodeStart":456,"sourceCodeEnd":492,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Hosting.Foundry/Toolbox/FoundryToolboxReconciler.cs#L456-L492","documentation":"ValidateOwnedDefault checks whether the Toolbox's current default version carries Aspire management metadata. When invoked during a concurrent-change check (concurrentChange: true) the default not being Aspire-managed throws this error, meaning the Toolbox survived but its default was repointed to a version Aspire did not create or no longer controls. In the non-concurrent path the same condition yields the 'already exists but is not managed by Aspire' error instead. Either way, Aspire refuses to proceed rather than silently operating on an unmanaged default.","triggerScenarios":"PromoteOwnedVersionAsync or VerifyReusedDefaultAsync re-reads a Toolbox after promotion/during reconcile and finds state.DefaultVersion points at a version without Aspire ownership metadata; thrown when concurrentChange is true (i.e., the resource was Aspire-managed earlier in the same operation).","commonSituations":"Someone manually creates/promotes a new Toolbox version in the Foundry portal and sets it as default while a deploy is in flight; an external tool (script, IaC) repoints the default; a partially-failed external rollout leaves an unmanaged version as default.","solutions":["Identify the unmanaged default version in the Foundry portal and either re-promote the Aspire-managed version as default or let Aspire re-run the reconcile to restore it","Stop external tooling or manual portal edits that repoint the default while Aspire deploys","If the new default is intentional, adopt it deliberately by declaring that version in your AppHost so Aspire manages it","Re-run the deploy; if it recurs, inspect who/what is mutating the Toolbox between operations"],"exampleFix":"// before: default repointed manually to unmanaged version '9.9.9'\n\n// after: declare the intended managed version in the AppHost\n#pragma warning disable ASPIREFOUNDRY001\ncbuilder.AddFoundryToolbox(\"my-toolbox\", version: \"1.3.0\");","handlingStrategy":"validation","validationCode":"// Detect unmanaged defaults before deploying\nvar state = await administration.GetAsync(name, ct);\nbool defaultIsManaged = state?.Versions.FirstOrDefault(v => v.Version == state.DefaultVersion)\n    ?.Metadata.ContainsKey(\"aspire.managed\") ?? false;\nif (state is not null && !defaultIsManaged)\n    throw new InvalidOperationException($\"Toolbox '{name}' default is not Aspire-managed; adopt or repoint it first.\");","typeGuard":"bool IsAspireManagedDefault(FoundryToolboxState state) =>\n    state.Versions.FirstOrDefault(v => v.Version == state.DefaultVersion)?.Metadata.Count > 0;","tryCatchPattern":"try\n{\n    await reconciler.ReconcileAsync(definition, ct);\n}\ncatch (InvalidOperationException)\n{\n    // Toolbox default was repointed by an external actor.\n    // Decide: adopt the new version in the AppHost, or restore via redeploy.\n}","preventionTips":["Manage Toolbox versions exclusively through the AppHost, not the portal","Audit the Toolbox default version before scheduled deploys","Restrict portal write access to the deployment identity"],"tags":["concurrency","azure","versioning","reconciliation"],"backgroundTag":"concurrent-change-detected","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"}