microsoft/aspire · error · InvalidOperationException

A ConfigureRadiusInfrastructure callback left container

Error message

A ConfigureRadiusInfrastructure callback left container '{container.ContainerMapKey}' with more than one port on {literalPort}/{literalProtocol} (for example port '{portName}'). The Radius container recipe creates one Kubernetes Service port per declared port, so duplicate (containerPort, protocol) pairs would emit conflicting Service ports. Remove the duplicate port.

What it means

RadiusInfrastructureBuilder collects every declared (containerPort, protocol) pair per container after callback processing and rejects duplicates. The Radius container recipe creates one Kubernetes Service port per declared port, so two ports on the same containerPort/protocol would emit conflicting Service port entries. The throw prevents generating a broken Service.

Solutions

  1. Remove the duplicate port entry so each (containerPort, protocol) pair appears at most once per container
  2. Merge the two named ports into a single port entry with one name
  3. If two distinct endpoints are needed, give them distinct containerPort values

Example fix

// before
container.Ports["http"] = new() { ContainerPort = 8080, Protocol = "tcp" };
container.Ports["web"] = new() { ContainerPort = 8080, Protocol = "tcp" };
// after
container.Ports["http"] = new() { ContainerPort = 8080, Protocol = "tcp" };
Defensive patterns

Strategy: validation

Validate before calling

var seen = new HashSet<(int, string)>();
foreach (var p in containerPorts)
    if (!seen.Add((p.ContainerPort, p.Protocol)))
        throw new InvalidOperationException($"Duplicate port {p.ContainerPort}/{p.Protocol}.");

Prevention

When it happens

Trigger: A ConfigureRadiusInfrastructure callback adds or leaves multiple port entries that resolve to the same literal (containerPort, protocol) tuple — detected when seenPorts.Add returns false. Ports skipped by other filters are ignored via 'continue'.

Common situations: Adding a second named port that points at the same containerPort and protocol (e.g. 'http' and 'web' both on 8080/tcp), usually from copy-pasted port declarations in the callback.

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/4ef20f76fbe0e19f. Report an issue: GitHub.

Appendix: source

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

            foreach (var (portName, portValue) in container.Ports)
            {
                if (portValue.Value is not { } port)
                {
                    continue;
                }

                // Only literal ports can collide deterministically; non-literal (expression-backed)
                // ports on callback-added containers are the customization's own responsibility and
                // can't be compared here.
                if (((IBicepValue)port.ContainerPort).LiteralValue is not int literalPort ||
                    ((IBicepValue)port.Protocol).LiteralValue is not string literalProtocol)
                {
                    continue;
                }

                if (!seenPorts.Add((literalPort, literalProtocol)))
                {
                    throw new InvalidOperationException(
                        $"A ConfigureRadiusInfrastructure callback left container '{container.ContainerMapKey}' with " +
                        $"more than one port on {literalPort}/{literalProtocol} (for example port '{portName}'). The " +
                        $"Radius container recipe creates one Kubernetes Service port per declared port, so duplicate " +
                        $"(containerPort, protocol) pairs would emit conflicting Service ports. Remove the duplicate port.");
                }
            }
        }

        ValidateContainerEnvVarForms(options);
    }

    /// <summary>
    /// Rejects a container environment entry that carries neither exactly a <c>value</c> nor a
    /// complete <c>valueFrom.secretKeyRef</c>.
    /// </summary>
    /// <remarks>
    /// The Radius container schema models each <c>env</c> entry as one form or the other, and the
    /// Kubernetes API server rejects a container env var that carries both <c>value</c> and

View on GitHub (pinned to 25830f84bd)