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

  1. 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)
  2. Verify the application serving the challenge endpoint is running and reachable at challengeUrl
  3. Fix authentication/credentials used between the CLI and the management challenge endpoint
  4. 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

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

Related errors


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/3bca1267ee9fc551. Report an issue: GitHub.