microsoft/aspire · error · InvalidOperationException

A ConfigureRadiusInfrastructure callback replaced the…

Error message

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.

What it means

RadiusInfrastructureBuilder validates that ConfigureRadiusInfrastructure callbacks did not replace a container port's protocol with a computed Bicep expression. Service discovery assumes the literal protocol (e.g. 'tcp' or 'udp') recorded on the Aspire resource; a deploy-time-computed protocol cannot be reconciled with the already-generated 'services__*' connection strings. The builder throws to block an unreproducible manifest.

Solutions

  1. Remove the callback line that changes the port protocol, keeping the literal protocol from the Aspire resource
  2. If a different protocol is required, set it on the Aspire endpoint/resource definition itself so service discovery emits the correct value
  3. Use a plain string literal only on the resource definition, never an expression-backed BicepValue in the callback

Example fix

// before
port.Protocol = BicepValue<string>.Create(moduleOutputProtocol);
// after
// remove the protocol assignment; protocol stays as declared on the endpoint
Defensive patterns

Strategy: validation

Validate before calling

var v = (IBicepValue)port.Protocol;
if (v.Expression is not null || v.LiteralValue is not string)
    throw new InvalidOperationException($"Port '{portName}' must keep a literal protocol string.");

Type guard

static bool IsLiteralStringProtocol(IBicepValue v) => v.Expression is null && v.LiteralValue is string;

Prevention

When it happens

Trigger: A ConfigureRadiusInfrastructure callback assigns port.Protocol a BicepValue<string> backed by an Expression, or a value whose LiteralValue is not a string. Triggered when protocolValueBicep.Expression is not null or LiteralValue is not a string.

Common situations: Developers try to derive the protocol from a Bicep parameter or module output, or set the protocol with a generic BicepValue helper that produces an expression rather than a literal.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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

Appendix: source

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

                // Reject a non-literal port/protocol: service discovery is a fixed literal, so a
                // callback that swaps in a Bicep expression could evaluate to a different value at
                // deploy time, reintroducing exactly the mismatch this guard prevents. An
                // expression-backed BicepValue<int> reports a default LiteralValue of 0 (not null),
                // so a non-null Expression is the reliable "non-literal" signal, not the LiteralValue.
                var portValueBicep = (IBicepValue)port.ContainerPort;
                if (portValueBicep.Expression is not null || portValueBicep.LiteralValue is not int literalPort)
                {
                    throw new InvalidOperationException(
                        $"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

View on GitHub (pinned to 25830f84bd)