microsoft/aspire · error · InvalidOperationException
ClusterIssuer ' ' has no solvers configured. Add at least…
Error message
ClusterIssuer '{issuer.Name}' has no solvers configured. Add at least one solver via WithHttp01Solver(). What it means
After verifying the issuer has a spec, ApplyClusterIssuerAsync also requires at least one challenge solver (e.g. HTTP-01) because cert-manager cannot complete ACME challenges without one. If issuer.Solvers is empty the publisher throws InvalidOperationException.
Solutions
- Add .WithHttp01Solver() to the ClusterIssuer builder chain.
- If using DNS challenges via a custom solver type, ensure the corresponding solver extension is applied.
- Verify no code path clears the Solvers collection after configuration.
Example fix
// before
certManager.AddClusterIssuer("letsencrypt").WithLetsEncryptProduction(email);
// after
certManager.AddClusterIssuer("letsencrypt")
.WithLetsEncryptProduction(email)
.WithHttp01Solver(); Defensive patterns
Strategy: validation
Validate before calling
if (issuer.Solvers.Count == 0) issuerBuilder.WithHttp01Solver(); // ensure a solver exists
Prevention
- Treat AddClusterIssuer + config + WithHttp01Solver as an atomic chain.
- Add a helper method that builds fully-configured issuers to avoid partial setup.
When it happens
Trigger: Publishing/running a model where an issuer was configured with an ACME spec (e.g. WithLetsEncryptProduction) but no solver was added via WithHttp01Solver() (or other solver extension).
Common situations: Developers configure the ACME account/email but forget the challenge solver step, or remove solver configuration during refactoring.
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
- Unknown solver type ' ' on issuer ' '.
- cert-manager resource name
- ClusterIssuer ' ' has no spec. Configure it with…
- Gateway ' ' is in Kubernetes environment ' ' but issuer ' '…
- kubectl apply for ClusterIssuer
AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16).
Data as JSON: /api/errors/8d1ffea9318abde1.
Report an issue: GitHub.
Appendix: source
Thrown at src/Aspire.Hosting.Kubernetes/CertManagerExtensions.cs:433
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);
// Write to a temp file and apply. kubectl apply -f - via stdin would avoid the
// temp file but ProcessUtil.Run doesn't expose a stdin pipe; Directory.CreateTempSubdirectory
// is the standard temp pattern in this codebase (see EnsureBootstrapTlsSecretAsync).
var tempDir = Directory.CreateTempSubdirectory(".aspire-cm-issuer");
try
{View on GitHub (pinned to 25830f84bd)