microsoft/aspire · error · InvalidOperationException
Gateway ' ' is in Kubernetes environment ' ' but issuer ' '…
Error message
Gateway '{builder.Resource.Name}' is in Kubernetes environment '{gatewayEnvironment.Name}' but issuer '{issuer.Resource.Name}' belongs to cert-manager installation '{issuer.Resource.Parent.Name}' in environment '{issuerEnvironment.Name}'. cert-manager is per-cluster, so an issuer can only be used by gateways in the same Kubernetes environment. What it means
WithTls validates that the cert-manager ClusterIssuer being referenced lives in the same Kubernetes environment (cluster) as the Gateway resource. cert-manager issuers are cluster-scoped, so a gateway in one environment cannot use an issuer installed by a cert-manager resource in a different environment; Aspire throws InvalidOperationException when the parent environments differ (compared with ResourceNameComparer).
Solutions
- Create a cert-manager instance in the same Kubernetes environment as the gateway and use its issuer.
- Move the WithTls call to use an issuer whose AddCertManager parent matches the gateway's environment.
- If the issuer should be shared, restructure so both gateways are in one environment.
Example fix
// before
var envA = builder.AddKubernetesEnvironment("env-a");
var envB = builder.AddKubernetesEnvironment("env-b");
var cm = envA.AddCertManager("cm");
envB.AddGateway("gw").WithTls(cm.AddClusterIssuer("letsencrypt"));
// after
var cmB = envB.AddCertManager("cm-b");
envB.AddGateway("gw").WithTls(cmB.AddClusterIssuer("letsencrypt")); Defensive patterns
Strategy: validation
Validate before calling
bool SameEnvironment(GatewayResource g, CertManagerIssuerResource i) =>
string.Equals(g.Parent?.Name, i.Resource.Parent?.Parent?.Name, StringComparison.OrdinalIgnoreCase); Try / catch
try { gateway.WithTls(issuer); }
catch (InvalidOperationException ex) when (ex.Message.Contains("same Kubernetes environment")) { /* reconfigure issuer in gateway's environment */ } Prevention
- Define cert-manager and gateways inside the same AddKubernetesEnvironment scope.
- Keep one cert-manager instance per Kubernetes environment.
When it happens
Trigger: Calling gateway.WithTls(issuer) where issuer was created by an AddCertManager call on a different Kubernetes environment resource than the one containing the gateway.
Common situations: Multi-environment app models where a shared cert-manager was defined in environment A and gateways were defined in environment B; refactoring that moved a gateway to a new environment without moving the issuer reference.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- cert-manager resource name
- ClusterIssuer ' ' has no solvers configured. Add at least…
- ClusterIssuer ' ' has no spec. Configure it with…
- Gateway ' ' was not assigned a hostname address within the…
- kubectl apply for ClusterIssuer
AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16).
Data as JSON: /api/errors/88d3627f309c5c7d.
Report an issue: GitHub.
Appendix: source
Thrown at src/Aspire.Hosting.Kubernetes/CertManagerExtensions.cs:343
/// but type-safe and refactor-friendly. Throws if the gateway and the issuer's
/// cert-manager installation are not part of the same Kubernetes environment, since
/// cert-manager is per-cluster and would otherwise silently produce an unsatisfiable
/// TLS configuration.
/// </remarks>
[AspireExport("withGatewayTlsIssuer")]
public static IResourceBuilder<KubernetesGatewayResource> WithTls(
this IResourceBuilder<KubernetesGatewayResource> builder,
IResourceBuilder<CertManagerIssuerResource> issuer)
{
ArgumentNullException.ThrowIfNull(builder);
ArgumentNullException.ThrowIfNull(issuer);
var gatewayEnvironment = builder.Resource.Parent;
var issuerEnvironment = issuer.Resource.Parent.Parent;
var nameComparer = new ResourceNameComparer();
if (!nameComparer.Equals(gatewayEnvironment, issuerEnvironment))
{
throw new InvalidOperationException(
$"Gateway '{builder.Resource.Name}' is in Kubernetes environment '{gatewayEnvironment.Name}' but issuer " +
$"'{issuer.Resource.Name}' belongs to cert-manager installation '{issuer.Resource.Parent.Name}' in environment " +
$"'{issuerEnvironment.Name}'. cert-manager is per-cluster, so an issuer can only be used by gateways in the " +
"same Kubernetes environment.");
}
return builder
.WithTls()
.WithGatewayAnnotation(ClusterIssuerAnnotationKey, issuer.Resource.Name);
}
private static Task<IEnumerable<PipelineStep>> BuildIssuerApplySteps(
CertManagerResource certManager,
string chartName)
{
var steps = new List<PipelineStep>();
foreach (var issuer in certManager.Issuers)View on GitHub (pinned to 25830f84bd)