{"record":{"id":"f6c5b9dd92aa6788","repo":"microsoft/aspire","slug":"aspireradius028","errorCode":"ASPIRERADIUS028","errorMessage":"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.","messagePattern":"Recipe parameters bound to Aspire parameters '(.+?)' and '(.+?)' both map to the Bicep identifier '(.+?)'\\. Rename one of the parameters so they produce distinct Bicep identifiers\\. Diagnostic: ASPIRERADIUS028\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Aspire.Hosting.Radius/Publishing/RadiusInfrastructureBuilder.cs","lineNumber":5441,"sourceCode":"            // GetOrAddEnvParameter already performs in the opposite direction, so the two\n            // allocators agree regardless of which one runs first. Deliberately not cached in\n            // _recipeParameters: the env allocator emits it through options.Parameters, and the\n            // deploy binding was already recorded in _deployParametersByIdentifier, which\n            // RecordDeployParameters merges with the recipe bindings.\n            if (_envParametersByName.TryGetValue(parameter.Name, out var envParameter))\n            {\n                return envParameter;\n            }\n\n            var identifier = BicepPostProcessor.SanitizeIdentifier(parameter.Name);\n\n            // Two distinct parameter names can sanitize to the same Bicep identifier (e.g.\n            // \"my-key\" and \"my.key\" both become \"my_key\"). Emitting two `param my_key`\n            // declarations produces invalid Bicep, so fail with an actionable diagnostic\n            // (ASPIRERADIUS028) instead.\n            if (_recipeParameterIdentifiers.TryGetValue(identifier, out var existingName))\n            {\n                throw new InvalidOperationException(\n                    $\"Recipe parameters bound to Aspire parameters '{existingName}' and '{parameter.Name}' both \" +\n                    $\"map to the Bicep identifier '{identifier}'. Rename one of the parameters so they produce \" +\n                    \"distinct Bicep identifiers. Diagnostic: ASPIRERADIUS028.\");\n            }\n\n            provisioningParameter = new ProvisioningParameter(identifier, typeof(string))\n            {\n                IsSecure = parameter.Secret,\n            };\n            _recipeParameters[parameter.Name] = provisioningParameter;\n            _recipeParameterIdentifiers[identifier] = parameter.Name;\n            // Remember the originating ParameterResource keyed by the Bicep identifier so the\n            // deploy step can pass `--parameters <identifier>=<value>` for this valueless param.\n            _recipeParameterBindings[identifier] = parameter;\n        }\n\n        return provisioningParameter;\n    }","sourceCodeStart":5423,"sourceCodeEnd":5459,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Hosting.Radius/Publishing/RadiusInfrastructureBuilder.cs#L5423-L5459","documentation":"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.","triggerScenarios":"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'.","commonSituations":"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.","solutions":["Rename one of the conflicting Aspire parameters so the sanitized identifiers differ","Use a value-parameter instead of a named parameter for one of the two bindings","Check all WithParameter names feeding Radius recipe parameters for collisions before publishing"],"exampleFix":"// before\nbuilder.AddParameter(\"my-key\"); builder.AddParameter(\"my.key\"); // both -> my_key\n// after\nbuilder.AddParameter(\"my-key\"); builder.AddParameter(\"my-key-2\");","handlingStrategy":"validation","validationCode":"static string Sanitize(string name) => new string(name.Select(c => char.IsLetterOrDigit(c) ? c : '_').ToArray()).ToLowerInvariant();\nvar dupes = paramNames.GroupBy(Sanitize).Where(g => g.Count() > 1).ToList();\nif (dupes.Count > 0) throw new Exception($\"Parameters collide on Bicep identifier: {string.Join(\", \", dupes.Select(d => string.Join(\"/\", d)))}\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Sanitize parameter names (lowercase, single-underscore separators) before choosing them","Grep the AppHost for parameters differing only by '-', '.', '_'","Add a startup check that all WithParameter names sanitize to unique identifiers"],"tags":["radius","naming-collision","bicep","diagnostic"],"backgroundTag":"invalid-identifier-format","analyzedSha":"25830f84bd145686607ad00c057b3f84e2e51d43","analyzedAt":"2026-09-16T11:10:06.193Z","contentChangedAt":"2026-09-16T11:10:06.193Z","schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}