microsoft/aspire · error · InvalidOperationException
A ConfigureRadiusInfrastructure callback replaced port
Error message
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. What it means
During publish, RadiusInfrastructureBuilder validates that ConfigureRadiusInfrastructure callbacks did not alter container ports. Aspire service discovery has already emitted the literal port number into consumers' 'services__*' environment variables, so a port replaced with a computed Bicep expression (or a non-int value) cannot be reconciled. The builder throws this InvalidOperationException to prevent publishing a manifest whose Service discovery would point at a port that differs from the deployed container port.
Solutions
- Remove the ConfigureRadiusInfrastructure callback line that changes the container port so the original literal port is kept
- If a different port is genuinely required, change the port on the Aspire resource itself (before service discovery values are computed) rather than via the callback
- Keep any callback edits limited to properties Aspire does not cross-check, like image or env additions
Example fix
// before
callback: (builder) =>
{
container.Ports["http"].ContainerPort = BicepValue<int>.Create(myOutput); // computed
}
// after
callback: (builder) =>
{
// leave the literal ContainerPort set on the resource untouched
} Defensive patterns
Strategy: validation
Validate before calling
var v = (IBicepValue)port.ContainerPort;
if (v.Expression is not null || v.LiteralValue is not int)
throw new InvalidOperationException($"Port '{portName}' must keep its literal containerPort."); Type guard
static bool IsLiteralIntPort(IBicepValue v) => v.Expression is null && v.LiteralValue is int;
Prevention
- Never assign Bicep expressions to ContainerPort inside ConfigureRadiusInfrastructure callbacks
- Change ports on the Aspire resource definition, not in publish-time callbacks
- Remember service discovery burns in literal ports into 'services__*' variables before callbacks run
When it happens
Trigger: A ConfigureRadiusInfrastructure callback assigns port.ContainerPort a BicepValue<int> built from an expression (e.g. an output reference or computed BicepValue) instead of a literal int. The check is triggered when portValueBicep.Expression is not null or LiteralValue is not an int.
Common situations: Developers try to wire a port to a Bicep parameter/output, copy a port from another module's output, or use a BicepValue expression assuming deploy-time resolution works — but service discovery has already burned in the literal value at manifest generation time.
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 changed port
- A ConfigureRadiusInfrastructure callback replaced the…
- A ConfigureRadiusInfrastructure callback left container
- A ConfigureRadiusInfrastructure callback renamed container
- A ConfigureRadiusInfrastructure callback replaced container
AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16).
Data as JSON: /api/errors/ca1ab0a0b874d42f.
Report an issue: GitHub.
Appendix: source
Thrown at src/Aspire.Hosting.Radius/Publishing/RadiusInfrastructureBuilder.cs:4984
{
if (!container.Ports.TryGetValue(portName, out var portValue) || portValue.Value is not { } port)
{
throw new InvalidOperationException(
$"A ConfigureRadiusInfrastructure callback removed port '{portName}' from container " +
$"'{mapKey}'. Aspire service discovery already emitted this port ({expected.Port}) into " +
$"consumer 'services__*' variables, so removing it would break cross-container calls. " +
$"Remove the port change to keep service discovery consistent.");
}
// 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))
{View on GitHub (pinned to 25830f84bd)