microsoft/aspire · error · InvalidOperationException

ASPIRERADIUS028

ASPIRERADIUS028

Error message

Recipe parameters bound to Aspire parameters '{existingName}' and '{parameter.Name}' both map to the Bicep identifier '{identifier}'. Rename one of the parameters so they produce distinct Bicep identifiers. Diagnostic: ASPIRERADIUS028.

What it means

Two distinct Aspire parameter names can sanitize to the same Bicep identifier (e.g. 'my-key' and 'my.key' both become 'my_key'), which would emit duplicate 'param my_key' declarations and invalid Bicep. Aspire detects the collision in _recipeParameterIdentifiers and throws, tagging it with diagnostic code ASPIRERADIUS028.

Solutions

  1. Rename one of the conflicting Aspire parameters so the sanitized identifiers differ
  2. Use a value-parameter instead of a named parameter for one of the two bindings
  3. Check all WithParameter names feeding Radius recipe parameters for collisions before publishing

Example fix

// before
builder.AddParameter("my-key"); builder.AddParameter("my.key"); // both -> my_key
// after
builder.AddParameter("my-key"); builder.AddParameter("my-key-2");
Defensive patterns

Strategy: validation

Validate before calling

static string Sanitize(string name) => new string(name.Select(c => char.IsLetterOrDigit(c) ? c : '_').ToArray()).ToLowerInvariant();
var dupes = paramNames.GroupBy(Sanitize).Where(g => g.Count() > 1).ToList();
if (dupes.Count > 0) throw new Exception($"Parameters collide on Bicep identifier: {string.Join(", ", dupes.Select(d => string.Join("/", d)))}");

Prevention

When it happens

Trigger: Defining two recipe parameters bound to Aspire parameters whose names normalize to the same Bicep identifier (differing only by characters sanitized to '_', '-', '.'), e.g. 'my-key' and 'my.key' or 'MyKey'.

Common situations: Kebab-case vs dot-notation parameter names used across a solution converging to the same identifier when the Radius environment publishes; renaming one parameter into collision with an existing one.

Understand the failure class

Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.

Related errors


AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16). Data as JSON: /api/errors/f6c5b9dd92aa6788. Report an issue: GitHub.

Appendix: source

Thrown at src/Aspire.Hosting.Radius/Publishing/RadiusInfrastructureBuilder.cs:5441

            // GetOrAddEnvParameter already performs in the opposite direction, so the two
            // allocators agree regardless of which one runs first. Deliberately not cached in
            // _recipeParameters: the env allocator emits it through options.Parameters, and the
            // deploy binding was already recorded in _deployParametersByIdentifier, which
            // RecordDeployParameters merges with the recipe bindings.
            if (_envParametersByName.TryGetValue(parameter.Name, out var envParameter))
            {
                return envParameter;
            }

            var identifier = BicepPostProcessor.SanitizeIdentifier(parameter.Name);

            // Two distinct parameter names can sanitize to the same Bicep identifier (e.g.
            // "my-key" and "my.key" both become "my_key"). Emitting two `param my_key`
            // declarations produces invalid Bicep, so fail with an actionable diagnostic
            // (ASPIRERADIUS028) instead.
            if (_recipeParameterIdentifiers.TryGetValue(identifier, out var existingName))
            {
                throw new InvalidOperationException(
                    $"Recipe parameters bound to Aspire parameters '{existingName}' and '{parameter.Name}' both " +
                    $"map to the Bicep identifier '{identifier}'. Rename one of the parameters so they produce " +
                    "distinct Bicep identifiers. Diagnostic: ASPIRERADIUS028.");
            }

            provisioningParameter = new ProvisioningParameter(identifier, typeof(string))
            {
                IsSecure = parameter.Secret,
            };
            _recipeParameters[parameter.Name] = provisioningParameter;
            _recipeParameterIdentifiers[identifier] = parameter.Name;
            // Remember the originating ParameterResource keyed by the Bicep identifier so the
            // deploy step can pass `--parameters <identifier>=<value>` for this valueless param.
            _recipeParameterBindings[identifier] = parameter;
        }

        return provisioningParameter;
    }

View on GitHub (pinned to 25830f84bd)