microsoft/aspire · error · InvalidOperationException
ASPIRERADIUS056
ASPIRERADIUS056
Error message
Two Radius constructs emit the same Bicep identifier '{identifier}' ({existing} and {description}). Bicep symbolic names share a single flat namespace, so every emitted resource and parameter must have a distinct identifier. Rename the conflicting resource — note that resource names are sanitized to Bicep identifiers (e.g. 'my-x' and 'my.x' both become 'my_x'), and that the publisher reserves the identifiers 'app', 'app_legacy', and 'recipepack' for its synthesized constructs. Diagnostic: ASPIRERADIUS056. What it means
The Radius publisher emits resources and parameters into a single flat Bicep namespace, and ValidateUniqueIdentifiers (called by CompileBicep) throws InvalidOperationException with diagnostic ASPIRERADIUS056 when two constructs sanitize to the same Bicep identifier. Note that names are sanitized (my-x and my.x both become my_x) and 'app', 'app_legacy', and 'recipepack' are reserved by the publisher.
Solutions
- Rename one of the conflicting resources so the sanitized identifiers differ (e.g. 'my-x' vs 'my-x2', or use different word separators).
- Rename any resource that uses the reserved identifiers 'app', 'app_legacy', or 'recipepack'.
- Check the error message's '{existing} and {description}' text to identify exactly which two constructs collide and adjust the one you control.
- Verify each emitted resource/parameter name is unique after sanitization by mentally replacing '-', '.', and other non-identifier characters with '_'.
Example fix
// before
var a = builder.AddContainer("my-x", "image");
var b = builder.AddContainer("my.x", "image"); // both sanitize to my_x
// after
var a = builder.AddContainer("my-x", "image");
var b = builder.AddContainer("my-x2", "image"); Defensive patterns
Strategy: validation
Validate before calling
static string SanitizeBicepIdentifier(string name) =>
new string(name.Select(c => char.IsLetterOrDigit(c) || c == '_' ? c : '_').ToArray());
var reserved = new[] { "app", "app_legacy", "recipepack" };
var ids = resources.Select(r => SanitizeBicepIdentifier(r.Name)).ToList();
if (ids.Distinct().Count() != ids.Count || ids.Any(reserved.Contains))
throw new InvalidOperationException("Bicep identifier collision or reserved name detected"); Try / catch
try { PublishAsRadiusApp(...); }
catch (InvalidOperationException ex) when (ex.Message.Contains("ASPIRERADIUS056")) { logger.LogError(ex, "Duplicate Bicep identifiers in Radius publish"); throw; } Prevention
- Give resources names that remain distinct after sanitization (avoid relying on '-' vs '.' to differ)
- Never name resources 'app', 'app_legacy', or 'recipepack'
- Run a uniqueness check on sanitized resource names in CI before publishing
When it happens
Trigger: Two Radius resources whose names differ only by characters removed during sanitization (e.g. 'my-x' and 'my.x' → 'my_x'); a user resource named 'app', 'app_legacy', or 'recipepack' colliding with the publisher's synthesized constructs; a resource named identically to an emitted parameter.
Common situations: Renaming resources from dashes to dots for different environments while both remain in the model; creating a container literally named 'app'; renaming one of two colliding resources but leaving the other unchanged.
Related errors
- A ConfigureRadiusInfrastructure callback replaced port
- A ConfigureRadiusInfrastructure callback replaced the…
- ASPIRERADIUS028
- ASPIRERADIUS048
- ASPIRERADIUS093
AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16).
Data as JSON: /api/errors/40659c453fae065a.
Report an issue: GitHub.
Appendix: source
Thrown at src/Aspire.Hosting.Radius/Publishing/BicepPostProcessor.cs:313
{
BicepDictionary<object> nestedObject => nestedObject,
BicepList<object> nestedArray => nestedArray,
var scalar => new BicepValue<object>(scalar)
};
}
private static void ValidateUniqueIdentifiers(RadiusInfrastructureOptions options)
{
// Maps each claimed Bicep identifier to a human-readable description of the construct that
// first claimed it, so the diagnostic can name both sides of a collision. Ordinal because
// Bicep identifiers are case-sensitive.
var seen = new Dictionary<string, string>(StringComparer.Ordinal);
void Register(string identifier, string description)
{
if (seen.TryGetValue(identifier, out var existing))
{
throw new InvalidOperationException(
$"Two Radius constructs emit the same Bicep identifier '{identifier}' ({existing} and {description}). " +
"Bicep symbolic names share a single flat namespace, so every emitted resource and parameter must " +
"have a distinct identifier. Rename the conflicting resource — note that resource names are sanitized " +
"to Bicep identifiers (e.g. 'my-x' and 'my.x' both become 'my_x'), and that the publisher reserves the " +
"identifiers 'app', 'app_legacy', and 'recipepack' for its synthesized constructs. Diagnostic: ASPIRERADIUS056.");
}
seen[identifier] = description;
}
// Enumerate every collection added to the flat namespace in CompileBicep, in the same order.
foreach (var pack in options.RecipePacks)
{
Register(pack.BicepIdentifier, "a recipe pack");
}
foreach (var environment in options.Environments)
{View on GitHub (pinned to 25830f84bd)