quarkusio/quarkus · error · RuntimeException
Failed to respond to certificate authority challenge
Error message
Failed to respond to certificate authority challenge
What it means
To answer the HTTP-01 challenge, AcmeClient uploads the key authorization to the management challenge endpoint via an HTTP request and expects a 204 No Content response. If the server answers with any other status code, the client logs the status and throws this RuntimeException, meaning the challenge response could not be recorded and Let's Encrypt will not be able to validate the identifier.
Source
Thrown at extensions/tls-registry/cli/src/main/java/io/quarkus/tls/cli/letsencrypt/AcmeClient.java:188
if (managementClient != null) {
//TODO: Use JsonObject once POST is supported
//JsonObject challenge = new JsonObject().put("challenge-resource", token).put("challenge-content",
// selectedChallengeString);
HttpRequest<Buffer> request = managementClient.getAbs(challengeUrl);
request.addQueryParam("challenge-resource", token).addQueryParam("challenge-content", selectedChallengeString);
addKeyAndUser(request);
AUDIT.info("Uploading challenge to management endpoint - token: " + token.substring(0, Math.min(8, token.length()))
+ "..., endpoint: " + challengeUrl);
LOGGER.debugf("Sending token %s and challenge content to the management challenge endpoint", token,
selectedChallengeString);
HttpResponse<Buffer> response = await(request.send());
if (response.statusCode() != 204) {
AUDIT.error("Failed to upload challenge - status: " + response.statusCode() + ", endpoint: " + challengeUrl);
LOGGER.error("⚠️ Failed to upload challenge content to the management challenge endpoint, status code: "
+ response.statusCode());
throw new RuntimeException("Failed to respond to certificate authority challenge");
} else {
LOGGER.infof("\uD83D\uDD35 Challenge ready for token %s, waiting for Let's Encrypt to validate...", token);
}
}
return selectedChallenge;
}
@Override
public void cleanupAfterChallenge(AcmeAccount account, AcmeChallenge challenge) throws AcmeException {
LOGGER.info("\uD83D\uDD35 Performing cleanup after the challenge");
Assert.checkNotNullParam("account", account);
Assert.checkNotNullParam("challenge", challenge);
// ensure the token is valid before proceeding
String token = challenge.getToken();
if (!token.matches(TOKEN_REGEX)) {
throw new RuntimeException("Invalid certificate authority challenge");
}View on GitHub (pinned to e1c734241f)
Solutions
- Check the logged status code and the target application's logs for why the challenge upload failed (404 = wrong path, 401/403 = auth, 5xx = server error)
- Verify the application serving the challenge endpoint is running and reachable at challengeUrl
- Fix authentication/credentials used between the CLI and the management challenge endpoint
- Check proxies/ingress in front of the endpoint are forwarding (not redirecting or blocking) the request, then retry the operation
Example fix
// before: endpoint not exposed -> 404 -> RuntimeException // management endpoint missing from application config // after: ensure the challenge management endpoint is enabled and reachable quarkus.tls.lets-encrypt.challenge-endpoint.enabled=true # confirm: curl -i -X POST http://localhost:9000/q/... returns 204
Defensive patterns
Strategy: try-catch
Validate before calling
import java.net.http.*;
import java.net.URI;
static boolean challengeEndpointReachable(URI challengeUrl) {
try {
HttpClient client = HttpClient.newHttpClient();
HttpRequest req = HttpRequest.newBuilder(challengeUrl)
.method("HEAD", HttpRequest.BodyPublishers.noBody()).build();
HttpResponse<Void> resp = client.send(req, HttpResponse.BodyHandlers.discarding());
int s = resp.statusCode();
return s >= 200 && s < 300 || s == 405; // 405 = endpoint exists, method differs
} catch (Exception e) {
return false;
}
}
Try / catch
try {
acmeClient.proveIdentifierControl(identifier, account, ...);
} catch (RuntimeException e) {
if (e.getMessage().equals("Failed to respond to certificate authority challenge")) {
// check application logs for the logged HTTP status; fix endpoint/auth/proxy, then retry
} else {
throw e;
}
} Prevention
- Smoke-test the management challenge endpoint (expect 204) before starting the ACME flow
- Ensure the target application is running and the challenge endpoint is deployed at the exact challengeUrl path
- Verify credentials/auth tokens used by the CLI against the management endpoint
- Check reverse proxies/ingress forward POSTs without redirects, and monitor for 5xx on the endpoint
When it happens
Trigger: Calling proveIdentifierControl when the HTTP POST of the challenge content to challengeUrl returns a non-204 status — 404 if the challenge endpoint path is wrong, 401/403 on auth failure, 405 for a method mismatch, 5xx if the management endpoint is failing, or a proxy answering with 301/302 instead of forwarding.
Common situations: The application's challenge management endpoint not being deployed or mounted at the expected URL; authentication between the CLI and the management endpoint broken; a reverse proxy intercepting the challenge URL; the target application being down or redeployed mid-flow; firewall/ingress rules rewriting the request.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- Rate limit exceeded: too many ACME challenge requests. Wait
- Missing certificate authority challenge
- Invalid certificate authority challenge
- Failure to save the account
- Failed to obtain certificate: <reason>
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/3bca1267ee9fc551.
Report an issue: GitHub.