microsoft/aspire · error · InvalidOperationException
ClusterIssuer ' ' has no spec. Configure it with…
Error message
ClusterIssuer '{issuer.Name}' has no spec. Configure it with WithLetsEncryptProduction(), WithLetsEncryptStaging(), or WithAcmeServer(). What it means
During publishing, ApplyClusterIssuerAsync renders the ClusterIssuer manifest. If issuer.Spec is null — no ACME configuration was ever applied — there is nothing to serialize, so the publisher throws InvalidOperationException telling you which configuration methods to call.
Solutions
- Chain WithLetsEncryptProduction() on the ClusterIssuer builder for production Let's Encrypt.
- Use WithLetsEncryptStaging() while testing to avoid ACME rate limits.
- Use WithAcmeServer() for a custom ACME directory endpoint.
- Remove the issuer from the model if it is intentionally unconfigured.
Example fix
// before
var issuer = certManager.AddClusterIssuer("letsencrypt");
// after
var issuer = certManager.AddClusterIssuer("letsencrypt")
.WithLetsEncryptProduction("admin@example.com"); Defensive patterns
Strategy: validation
Validate before calling
if (issuer.Spec is null) issuerBuilder.WithLetsEncryptProduction(email); // configure before publish
Prevention
- Always chain a configuration method (WithLetsEncryptProduction/Staging/WithAcmeServer) onto AddClusterIssuer.
- Review issuer configuration in code review before publishing.
When it happens
Trigger: Publishing a model where AddClusterIssuer was called but neither WithLetsEncryptProduction(), WithLetsEncryptStaging(), nor WithAcmeServer() was invoked on the resulting issuer.
Common situations: Creating a ClusterIssuer placeholder and deferring configuration, or calling AddClusterIssuer without chaining any configuration extension.
Understand the failure class
Background: "X is required", "must be set", "cannot be empty": the missing-required-config error family, from Vertex AI project/location to WeChat keys — this error's family across 18 libraries.
Related errors
- kubectl apply for ClusterIssuer
- A ConfigureRadiusInfrastructure callback left container
- cert-manager resource name
- ClusterIssuer ' ' has no solvers configured. Add at least…
- Gateway ' ' is in Kubernetes environment ' ' but issuer ' '…
AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16).
Data as JSON: /api/errors/913cbc4445cdd1c2.
Report an issue: GitHub.
Appendix: source
Thrown at src/Aspire.Hosting.Kubernetes/CertManagerExtensions.cs:426
// not found) or fails. Deleting while cert-manager is still alive also lets the
// controller clean up the ACME account secret it created in the cert-manager
// namespace.
step.RequiredBy($"helm-uninstall-{chartName}");
step.RequiredBy(WellKnownPipelineSteps.Destroy);
steps.Add(step);
}
return Task.FromResult<IEnumerable<PipelineStep>>(steps);
}
private static async Task ApplyClusterIssuerAsync(
PipelineStepContext context,
CertManagerResource certManager,
CertManagerIssuerResource issuer)
{
if (issuer.Spec is null)
{
throw new InvalidOperationException(
$"ClusterIssuer '{issuer.Name}' has no spec. Configure it with WithLetsEncryptProduction(), " +
"WithLetsEncryptStaging(), or WithAcmeServer().");
}
if (issuer.Solvers.Count == 0)
{
throw new InvalidOperationException(
$"ClusterIssuer '{issuer.Name}' has no solvers configured. Add at least one " +
"solver via WithHttp01Solver().");
}
var environment = certManager.Parent;
var manifest = await BuildClusterIssuerManifestAsync(context.Model, certManager, issuer, context.Logger, context.CancellationToken)
.ConfigureAwait(false);
context.Logger.LogInformation(
"Applying cert-manager ClusterIssuer '{IssuerName}'.", issuer.Name);View on GitHub (pinned to 25830f84bd)