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
- Remove the callback line that changes the port protocol, keeping the literal protocol from the Aspire resource
- If a different protocol is required, set it on the Aspire endpoint/resource definition itself so service discovery emits the correct value
- 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
- Use plain string literals (e.g. "tcp") for port protocols, never expression-backed BicepValue<string>
- Set the protocol on the Aspire endpoint definition so it propagates everywhere consistently
- Audit callbacks for any BicepValue.Create calls on protocol properties
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
- A ConfigureRadiusInfrastructure callback replaced port
- A ConfigureRadiusInfrastructure callback changed port
- A ConfigureRadiusInfrastructure callback renamed container
- A ConfigureRadiusInfrastructure callback replaced container
- ASPIRERADIUS056
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 previouslyView on GitHub (pinned to 25830f84bd)