microsoft/aspire · error · InvalidOperationException
Toolbox ' ' changed concurrently: target version ' ' is no…
Error message
Toolbox '{name}' changed concurrently: target version '{version}' is no longer visible. No further changes were made. What it means
ValidatePromotionTarget confirms, before validating ownership, that the version Aspire just promoted still exists in the Toolbox's version list when the post-promotion state is read. If the target version was deleted concurrently, the reconciler throws this error and makes no further changes. It prevents Aspire from validating or reporting success for a version that no longer exists.
Solutions
- Re-run the deploy so Aspire re-publishes the version and completes the promotion
- Check whether a cleanup/retention job deletes Toolbox versions during deploys and reschedule or exclude Aspire-managed versions
- Coordinate with team members to avoid deleting versions while a deployment is running
- Serialize reconciles per environment with a pipeline concurrency group
Example fix
// before: cleanup job deletes versions older than 1 day, deploy promotes 1.3.0 published moments ago
// after: exclude the version being deployed from cleanup
// cleanup filter: delete versions NOT matching current AppHost-declared version
#pragma warning disable ASPIREFOUNDRY001
cbuilder.AddFoundryToolbox("my-toolbox", version: "1.3.0"); Defensive patterns
Strategy: retry
Validate before calling
// Confirm the target version exists before deploying
var state = await administration.GetAsync(definition.Name, ct);
bool versionVisible = state?.Versions.Any(v => v.Version == definition.Version) == true;
if (state is not null && !versionVisible) { /* version was deleted; let reconcile republish */ } Type guard
bool VersionExists(FoundryToolboxState? state, string version) =>
state?.Versions.Any(v => string.Equals(v.Version, version, StringComparison.Ordinal)) == true; Try / catch
try
{
await reconciler.ReconcileAsync(definition, ct);
}
catch (ConcurrentChangeException)
{
// Target version was deleted mid-flight; safe to re-run so it is republished.
await reconciler.ReconcileAsync(definition, ct);
} Prevention
- Exclude Aspire-managed/current versions from cleanup or retention jobs
- Freeze version deletions during deployment windows
- Use a single owner (Aspire) for Toolbox version lifecycle
When it happens
Trigger: PromoteOwnedVersionAsync calls ValidatePromotionTarget with the promoted version string, and state.Versions contains no entry whose Version matches — i.e., the version was deleted by another actor between promotion and the post-promotion GetAsync read.
Common situations: A retention/cleanup policy or pipeline deletes old Toolbox versions while a deploy promotes one; another developer removes the version in the portal mid-deploy; a racing Aspire reconcile deletes the version it previously published.
Related errors
- Toolbox ' ' changed concurrently: default version ' ' is…
- Toolbox ' ' changed concurrently: version ' ' was promoted…
- 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/461f3f935c25f723.
Report an issue: GitHub.
Appendix: source
Thrown at src/Aspire.Hosting.Foundry/Toolbox/FoundryToolboxReconciler.cs:492
throw CreateConcurrentChangeException(
name,
$"default version '{state.DefaultVersion}' is not managed by Aspire");
}
throw new InvalidOperationException(
$"Toolbox '{name}' already exists but is not managed by Aspire. No changes were made.");
}
private static void ValidatePromotionTarget(
FoundryToolboxDeploymentDefinition definition,
FoundryToolboxState state,
string version)
{
var target = state.Versions.FirstOrDefault(candidate =>
string.Equals(candidate.Version, version, StringComparison.Ordinal));
if (target is null)
{
throw CreateConcurrentChangeException(
definition.Name,
$"target version '{version}' is no longer visible");
}
if (!IsAspireManaged(target) ||
!target.Metadata.TryGetValue(
FoundryToolboxDeploymentDefinition.ConfigurationHashMetadataKey,
out var configurationHash) ||
!string.Equals(configurationHash, definition.ConfigurationHash, StringComparison.Ordinal))
{
throw CreateConcurrentChangeException(
definition.Name,
$"target version '{version}' no longer has the expected Aspire ownership and configuration");
}
}
private static bool IsAspireManaged(FoundryToolboxVersionState version) =>
version.Metadata.TryGetValue(View on GitHub (pinned to 25830f84bd)