microsoft/aspire · error · InvalidOperationException

A ConfigureRadiusInfrastructure callback changed port

Error message

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.

What it means

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.

Solutions

  1. Revert the port/protocol change in the callback so the values match what Aspire emitted (expected.Port / expected.Protocol)
  2. Change the port or protocol on the Aspire resource/endpoint definition instead, so both the container and all consumers are updated consistently
  3. Delete the ConfigureRadiusInfrastructure callback if it exists only to adjust ports

Example fix

// before
container.Ports["http"].ContainerPort = 8080; // expected 8000
// after
// no port change in callback; declare the desired port on the Aspire endpoint instead
Defensive patterns

Strategy: validation

Validate before calling

if (literalPort != expected.Port || !string.Equals(literalProtocol, expected.Protocol, StringComparison.Ordinal))
    throw new InvalidOperationException($"Port '{portName}' must stay {expected.Port}/{expected.Protocol}.");

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.

Related errors


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

Appendix: source

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

                        $"A ConfigureRadiusInfrastructure callback replaced port '{portName}' on container " +
                        $"'{mapKey}' with a non-literal Bicep expression. Aspire service discovery already emitted " +
                        $"the literal port {expected.Port} into consumer 'services__*' variables and cannot follow a " +
                        $"computed port, so a computed containerPort is not supported. Remove the port change.");
                }

                var protocolValueBicep = (IBicepValue)port.Protocol;
                if (protocolValueBicep.Expression is not null || protocolValueBicep.LiteralValue is not string literalProtocol)
                {
                    throw new InvalidOperationException(
                        $"A ConfigureRadiusInfrastructure callback replaced the protocol of port '{portName}' on " +
                        $"container '{mapKey}' with a non-literal Bicep expression. Aspire service discovery assumes " +
                        $"the literal protocol '{expected.Protocol}', so a computed protocol is not supported. Remove " +
                        $"the protocol change.");
                }

                if (literalPort != expected.Port || !string.Equals(literalProtocol, expected.Protocol, StringComparison.Ordinal))
                {
                    throw new InvalidOperationException(
                        $"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.");
                }
            }
        }

        // Validate the FINAL container set. A callback can add the first port to a previously
        // portless container, add a new container, or add a second endpoint. Once a container
        // declares ports the recipe creates a Service, so re-check the two things the recipe cares
        // about on the post-callback state: the Service name fits the Kubernetes limit, and the
        // container's ports are unique by (containerPort, protocol).
        foreach (var container in options.Containers)
        {
            if (container.Ports.Count == 0)
            {
                continue;

View on GitHub (pinned to 25830f84bd)