{"record":{"id":"570cd4df3b6a2b28","repo":"microsoft/aspire","slug":"a-configureradiusinfrastructure-callback-changed-port","errorCode":null,"errorMessage":"A ConfigureRadiusInfrastructure callback changed port '{portName}' on container '{mapKey}' from {expected.Port}/{expected.Protocol} to {literalPort}/{literalProtocol}. Aspire service discovery already emitted {expected.Port}/{expected.Protocol} into consumer 'services__*' variables, so this would break cross-container calls. Remove the port change.","messagePattern":"A ConfigureRadiusInfrastructure callback changed port '(.+?)' on container '(.+?)' from (.+?)/(.+?) to (.+?)/(.+?)\\. Aspire service discovery already emitted (.+?)/(.+?) into consumer 'services__\\*' variables, so this would break cross-container calls\\. Remove the port change\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Aspire.Hosting.Radius/Publishing/RadiusInfrastructureBuilder.cs","lineNumber":5003,"sourceCode":"                        $\"A ConfigureRadiusInfrastructure callback replaced port '{portName}' on container \" +\n                        $\"'{mapKey}' with a non-literal Bicep expression. Aspire service discovery already emitted \" +\n                        $\"the literal port {expected.Port} into consumer 'services__*' variables and cannot follow a \" +\n                        $\"computed port, so a computed containerPort is not supported. Remove the port change.\");\n                }\n\n                var protocolValueBicep = (IBicepValue)port.Protocol;\n                if (protocolValueBicep.Expression is not null || protocolValueBicep.LiteralValue is not string literalProtocol)\n                {\n                    throw new InvalidOperationException(\n                        $\"A ConfigureRadiusInfrastructure callback replaced the protocol of port '{portName}' on \" +\n                        $\"container '{mapKey}' with a non-literal Bicep expression. Aspire service discovery assumes \" +\n                        $\"the literal protocol '{expected.Protocol}', so a computed protocol is not supported. Remove \" +\n                        $\"the protocol change.\");\n                }\n\n                if (literalPort != expected.Port || !string.Equals(literalProtocol, expected.Protocol, StringComparison.Ordinal))\n                {\n                    throw new InvalidOperationException(\n                        $\"A ConfigureRadiusInfrastructure callback changed port '{portName}' on container \" +\n                        $\"'{mapKey}' from {expected.Port}/{expected.Protocol} to {literalPort}/{literalProtocol}. \" +\n                        $\"Aspire service discovery already emitted {expected.Port}/{expected.Protocol} into consumer \" +\n                        $\"'services__*' variables, so this would break cross-container calls. Remove the port change.\");\n                }\n            }\n        }\n\n        // Validate the FINAL container set. A callback can add the first port to a previously\n        // portless container, add a new container, or add a second endpoint. Once a container\n        // declares ports the recipe creates a Service, so re-check the two things the recipe cares\n        // about on the post-callback state: the Service name fits the Kubernetes limit, and the\n        // container's ports are unique by (containerPort, protocol).\n        foreach (var container in options.Containers)\n        {\n            if (container.Ports.Count == 0)\n            {\n                continue;","sourceCodeStart":4985,"sourceCodeEnd":5021,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Hosting.Radius/Publishing/RadiusInfrastructureBuilder.cs#L4985-L5021","documentation":"RadiusInfrastructureBuilder compares each port's literal containerPort/protocol after a ConfigureRadiusInfrastructure callback against the expected values Aspire recorded when service discovery emitted 'services__*' variables for consumers. If the callback changed either value, the deployed Kubernetes Service would no longer match the endpoints consumers already received, silently breaking cross-container calls. The builder throws to stop publication of the inconsistent manifest.","triggerScenarios":"A callback assigns a literal int to port.ContainerPort or a literal string to port.Protocol that differs from the expected values (expected.Port / expected.Protocol). The comparison uses ordinal equality on the protocol.","commonSituations":"Developers remap a container port inside a container (e.g. to 8080) or flip http to tcp in a ConfigureRadiusInfrastructure callback, not realizing the port value was already propagated to consumers' environment variables.","solutions":["Revert the port/protocol change in the callback so the values match what Aspire emitted (expected.Port / expected.Protocol)","Change the port or protocol on the Aspire resource/endpoint definition instead, so both the container and all consumers are updated consistently","Delete the ConfigureRadiusInfrastructure callback if it exists only to adjust ports"],"exampleFix":"// before\ncontainer.Ports[\"http\"].ContainerPort = 8080; // expected 8000\n// after\n// no port change in callback; declare the desired port on the Aspire endpoint instead","handlingStrategy":"validation","validationCode":"if (literalPort != expected.Port || !string.Equals(literalProtocol, expected.Protocol, StringComparison.Ordinal))\n    throw new InvalidOperationException($\"Port '{portName}' must stay {expected.Port}/{expected.Protocol}.\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Treat ConfigureRadiusInfrastructure callbacks as read-only for ports and protocols","If a port must change, change it at the Aspire resource level so consumers' services__* values update too","Any port edit must happen before service discovery values are computed"],"tags":["radius","publish","port","service-discovery","config-drift"],"backgroundTag":"conflicting-config-options","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"}