microsoft/aspire · error · InvalidOperationException
Toolbox ' ' changed concurrently: version ' ' was promoted…
Error message
Toolbox '{name}' changed concurrently: version '{version}' was promoted, but version '{after.DefaultVersion}' is now the default. No further changes were made. What it means
After promoting a Toolbox version, PromoteOwnedVersionAsync verifies that the promoted version actually became the default by re-reading state and comparing after.DefaultVersion to the promoted version. If someone changed the default version concurrently (promoted a different version or reassigned the default), the reconciler throws this error instead of silently accepting a default it did not intend. This keeps Aspire's declared model authoritative over the deployed default.
Solutions
- Re-run the deploy so Aspire re-promotes the intended version and the default converges
- Find and stop the concurrent actor changing the default (portal action, second pipeline), then re-run
- Check the Toolbox's default version in the portal and align your AppHost's declared version with whoever should own it
- Serialize deployments per environment (pipeline concurrency group) to prevent competing promotions
Example fix
// before: concurrent deploys promote different versions
// pipeline A: version 1.2.0, pipeline B: version 1.3.0
// after: single serialized pipeline promoting the intended version
cbuilder.AddFoundryToolbox("my-toolbox", version: "1.3.0"); Defensive patterns
Strategy: retry
Validate before calling
// Before deploying, check the current default matches expectations
var state = await administration.GetAsync(definition.Name, ct);
if (state?.DefaultVersion is not null && state.DefaultVersion != definition.Version)
logger.LogWarning("Default version drift detected: {Default}", state.DefaultVersion); Try / catch
try
{
await reconciler.ReconcileAsync(definition, ct);
}
catch (ConcurrentChangeException)
{
// Someone changed the default concurrently; re-running converges the default.
await reconciler.ReconcileAsync(definition, ct);
} Prevention
- Run only one pipeline that promotes Toolbox versions per environment
- Avoid manual 'set default' actions in the portal during deployments
- Keep the AppHost-declared version as the single source of truth for the default
When it happens
Trigger: ReconcileAsync promotes definition version X, then the verification GetAsync shows a different DefaultVersion — caused by another actor promoting/setting a different default between the PromoteVersionAsync call and the verification read.
Common situations: Two developers or pipelines deploying different versions of the same Toolbox at once; a manual 'set as default' action in the Foundry portal during a deploy; a scheduled job pinning an older default while CI promotes a newer one.
Related errors
- Toolbox ' ' changed concurrently: default version ' ' is…
- Toolbox ' ' changed concurrently: target version ' ' is no…
- Toolbox ' ' changed concurrently: target version ' ' no…
- Toolbox ' ' changed concurrently: the Toolbox was no longer…
- Azure operation ' ' is already running or queued for the…
AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16).
Data as JSON: /api/errors/31538022c77ab8d4.
Report an issue: GitHub.
Appendix: source
Thrown at src/Aspire.Hosting.Foundry/Toolbox/FoundryToolboxReconciler.cs:456
definition.Name,
$"expected {expectedDefault}, but version '{before.DefaultVersion}' is now the default");
}
await administration.PromoteVersionAsync(
definition.Name,
version,
cancellationToken).ConfigureAwait(false);
var after = await administration.GetAsync(definition.Name, cancellationToken).ConfigureAwait(false)
?? throw CreateConcurrentChangeException(
definition.Name,
"the Toolbox was no longer visible after promotion");
ValidateOwnedDefault(definition.Name, after, concurrentChange: true);
ValidatePromotionTarget(definition, after, version);
if (!string.Equals(after.DefaultVersion, version, StringComparison.Ordinal))
{
throw CreateConcurrentChangeException(
definition.Name,
$"version '{version}' was promoted, but version '{after.DefaultVersion}' is now the default");
}
}
private static void ValidateOwnedDefault(
string name,
FoundryToolboxState state,
bool concurrentChange = false)
{
if (IsAspireManaged(state.Default))
{
return;
}
if (concurrentChange)
{
throw CreateConcurrentChangeException(View on GitHub (pinned to 25830f84bd)