microsoft/aspire · error · InvalidOperationException
Toolbox ' ' changed concurrently: target version ' ' no…
Error message
Toolbox '{name}' changed concurrently: target version '{version}' no longer has the expected Aspire ownership and configuration. No further changes were made. What it means
ValidatePromotionTarget verifies the promoted version is still Aspire-managed and that its stored configuration hash still matches the definition's ConfigurationHash. If the version's metadata was stripped (ownership removed) or its configuration was modified by another actor so the hash no longer matches, the reconciler throws this error and stops. It guarantees the version being promoted is exactly what Aspire published, not an externally mutated one.
Solutions
- Re-run the deploy so Aspire republishes the version with its own ownership metadata and configuration hash, then promotes it
- Identify and stop the external edit (portal change, script) that mutated the version's configuration or stripped metadata
- If the configuration change was intentional, update the AppHost definition so the configuration hash matches the desired state and redeploy
- Delete the drifted version (via Aspire or portal) and redeploy to recreate it cleanly
Example fix
// before: portal edit changed the version's settings after Aspire published it
// after: change the configuration in the AppHost instead, so the hash matches
cbuilder.AddFoundryToolbox("my-toolbox", version: "1.3.0", configure: t => t.WithSetting("maxInstances", 5)); Defensive patterns
Strategy: validation
Validate before calling
// Compare the deployed version's config hash with the desired definition before promoting
var state = await administration.GetAsync(definition.Name, ct);
var target = state?.Versions.FirstOrDefault(v => v.Version == definition.Version);
bool hashMatches = target?.Metadata.TryGetValue("aspire.configurationHash", out var hash) == true
&& string.Equals(hash, definition.ConfigurationHash, StringComparison.Ordinal);
if (target is not null && !hashMatches) { /* drift detected; redeploy to republish */ } Type guard
bool HasExpectedHash(FoundryToolboxVersionState v, string expected) =>
v.Metadata.TryGetValue("aspire.configurationHash", out var h) &&
string.Equals(h, expected, StringComparison.Ordinal); Try / catch
try
{
await reconciler.ReconcileAsync(definition, ct);
}
catch (ConcurrentChangeException)
{
// Version config was mutated externally; redeploying republishes it with correct metadata.
await reconciler.ReconcileAsync(definition, ct);
} Prevention
- Change Toolbox configuration only through the AppHost definition, never by direct portal edits
- Redeploy after any intentional configuration change so the stored hash matches
- Alert on metadata/ownership changes to Aspire-managed versions
When it happens
Trigger: PromoteOwnedVersionAsync reads the post-promotion Toolbox state and the target version fails IsAspireManaged, lacks FoundryToolboxDeploymentDefinition.ConfigurationHashMetadataKey metadata, or has a configuration hash differing from definition.ConfigurationHash.
Common situations: Someone edits the version's configuration in the Foundry portal between publish and promote; an external tool overwrites version settings or metadata; a hand-rolled script imports a same-named version without Aspire's metadata; partial manual rollback strips ownership tags.
Related errors
- Toolbox ' ' changed concurrently: default version ' ' is…
- Toolbox ' ' changed concurrently: target version ' ' is no…
- Toolbox ' ' changed concurrently: the Toolbox was no longer…
- Toolbox ' ' changed concurrently: version ' ' was promoted…
- Azure operation ' ' is already running or queued for the…
AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16).
Data as JSON: /api/errors/36570f756bd5ed6f.
Report an issue: GitHub.
Appendix: source
Thrown at src/Aspire.Hosting.Foundry/Toolbox/FoundryToolboxReconciler.cs:503
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(
FoundryToolboxDeploymentDefinition.ManagedByMetadataKey,
out var managedBy) &&
string.Equals(
managedBy,
FoundryToolboxDeploymentDefinition.ManagedByMetadataValue,
StringComparison.Ordinal);
private static InvalidOperationException CreateConcurrentChangeException(
string name,
string detail) =>
new(View on GitHub (pinned to 25830f84bd)