microsoft/aspire · error · InvalidOperationException
Toolbox ' ' changed concurrently: default version ' '…
Error message
Toolbox '{name}' changed concurrently: default version '{expectedDefaultVersion}' matched, but the Toolbox is no longer visible. No further changes were made. What it means
Foundry Toolbox administration has no conditional update/ETag support, so the reconciler re-reads the Toolbox immediately before reusing its default version and verifies it still matches the state observed during reconciliation. This error means the expected default version matched, but the Toolbox disappeared before the reconciler could act - a concurrent writer deleted it. No further changes were made, keeping the deployment safe.
Solutions
- Re-run the deployment - the reconciler is idempotent and will recreate the Toolbox.
- Serialize deployments to the same Foundry project/Toolbox (one at a time).
- Check the Azure portal/audit logs for who deleted the Toolbox mid-deployment.
- Use distinct Foundry projects or Toolbox names per environment/branch.
Defensive patterns
Strategy: retry
Validate before calling
// Pre-flight: confirm the Toolbox exists and matches expectations before deploying
var toolbox = await administration.GetAsync(toolboxName);
if (toolbox is null)
throw new InvalidOperationException($"Toolbox '{toolboxName}' is missing; another writer may have deleted it."); Try / catch
try
{
await deploy.RunAsync();
}
catch (Exception ex) when (ex.Message.Contains("changed concurrently"))
{
logger.LogWarning(ex, "Concurrent Toolbox change detected; retrying deployment once.");
await deploy.RunAsync(); // reconciler is idempotent
} Prevention
- Use CI concurrency groups so only one deployment targets a Foundry project at a time.
- Avoid manual portal edits to Toolboxes during deployments.
- Give each environment/branch its own Toolbox name or Foundry project.
When it happens
Trigger: VerifyReusedDefaultAsync calls administration.GetAsync(definition.Name) and gets null after the default version was observed to match; ReconcileAsync converts this into a concurrent-change error via CreateConcurrentChangeException.
Common situations: Two Aspire deployments/CI pipelines targeting the same Foundry project simultaneously, one deleting/recreating the Toolbox; manual deletion in the Azure portal during a deploy; cleanup scripts racing with deploy.
Related errors
- Toolbox ' ' changed concurrently: default version ' '…
- Toolbox ' ' changed concurrently: expected , but version '…
- Toolbox ' ' changed concurrently: version ' ' was created…
- Project ' ' does not have a valid connection string.
- Project ' ' does not have a valid endpoint.
AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16).
Data as JSON: /api/errors/56c6615dc73d7503.
Report an issue: GitHub.
Appendix: source
Thrown at src/Aspire.Hosting.Foundry/Toolbox/FoundryToolboxReconciler.cs:391
if (!string.Equals(existing.DefaultVersion, updated, StringComparison.Ordinal))
{
await PromoteOwnedVersionAsync(
definition,
updated,
existing.DefaultVersion,
cancellationToken).ConfigureAwait(false);
}
return new(updated, FoundryToolboxReconcileAction.CreatedAndPromoted);
}
private async Task VerifyReusedDefaultAsync(
FoundryToolboxDeploymentDefinition definition,
string expectedDefaultVersion,
CancellationToken cancellationToken)
{
var current = await administration.GetAsync(definition.Name, cancellationToken).ConfigureAwait(false)
?? throw CreateConcurrentChangeException(
definition.Name,
$"default version '{expectedDefaultVersion}' matched, but the Toolbox is no longer visible");
ValidateOwnedDefault(definition.Name, current, concurrentChange: true);
if (!string.Equals(current.DefaultVersion, expectedDefaultVersion, StringComparison.Ordinal) ||
!current.Default.Metadata.TryGetValue(
FoundryToolboxDeploymentDefinition.ConfigurationHashMetadataKey,
out var currentHash) ||
!string.Equals(currentHash, definition.ConfigurationHash, StringComparison.Ordinal))
{
throw CreateConcurrentChangeException(
definition.Name,
$"default version '{expectedDefaultVersion}' matched, but version '{current.DefaultVersion}' with a different configuration is now the default");
}
}
private async Task PromoteOwnedVersionAsync(
FoundryToolboxDeploymentDefinition definition,View on GitHub (pinned to 25830f84bd)