quarkusio/quarkus · error · ConfigurationException

Cannot use Let's Encrypt without the quarkus-vertx-http exte

Error message

Cannot use Let's Encrypt without the quarkus-vertx-http extension

What it means

During build, CertificatesProcessor.createManagementRoutes() wires an HTTP route for the ACME challenge. Let's Encrypt renewal requires serving HTTP-01 challenge responses, which is done through Vert.x Web's Router. If io.vertx.ext.web.Router is not present at runtime in the Quarkus classloader, the build fails with this ConfigurationException because the challenge route cannot be registered.

Source

Thrown at extensions/tls-registry/deployment/src/main/java/io/quarkus/tls/deployment/CertificatesProcessor.java:89

                .supplier(supplier)
                .scope(Singleton.class)
                .unremovable()
                .setRuntimeInit();

        syntheticBeans.produce(configurator.done());

        return new TlsRegistryBuildItem(supplier);
    }

    @Record(ExecutionTime.RUNTIME_INIT)
    @BuildStep(onlyIf = LetsEncryptEnabled.class)
    void createManagementRoutes(BuildProducer<RouteBuildItem> routes,
            LetsEncryptRecorder recorder,
            TlsRegistryBuildItem registryBuildItem) {

        // Check if Vert.x Web is present
        if (!QuarkusClassLoader.isClassPresentAtRuntime("io.vertx.ext.web.Router")) {
            throw new ConfigurationException("Cannot use Let's Encrypt without the quarkus-vertx-http extension");
        }

        recorder.initialize(registryBuildItem.registry());

        // Route to handle the Let's Encrypt challenge - primary HTTP server
        routes.produce(RouteBuildItem.newAbsoluteRoute("/.well-known/acme-challenge/:token")
                .withRequestHandler(recorder.challengeHandler())
                .build());

        // Route to configure the Let's Encrypt challenge - management server
        routes.produce(RouteBuildItem.newManagementRoute("lets-encrypt/challenge")
                .withRequestHandler(recorder.chalengeAdminHandler())
                .withRouteCustomizer(recorder.setupCustomizer())
                .build());

        // Route to refresh the certificates - management server
        routes.produce(RouteBuildItem.newManagementRoute("lets-encrypt/certs")
                .withRequestHandler(recorder.reload())

View on GitHub (pinned to e1c734241f)

Solutions

  1. Add the quarkus-vertx-http extension: ./mvnw quarkus:add-extension -Dextensions="vertx-http"
  2. If HTTP serving is impossible, disable Let's Encrypt management and provide the certificate out-of-band (quarkus.tls.*.key-store files)
  3. Remove the Let's Encrypt/ACME config block if the app genuinely never terminates HTTP
  4. Verify the dependency is in the runtime module, not only deployment, then rebuild

Example fix

// before (pom.xml)
<dependency><groupId>io.quarkus</groupId><artifactId>quarkus-tls-registry</artifactId></dependency>
// after
<dependency><groupId>io.quarkus</groupId><artifactId>quarkus-tls-registry</artifactId></dependency>
<dependency><groupId>io.quarkus</groupId><artifactId>quarkus-vertx-http</artifactId></dependency>
Defensive patterns

Strategy: validation

Validate before calling

// build-time guard: ensure vertx-http is on the classpath before enabling ACME
boolean hasVertxHttp = QuarkusClassLoader.isClassPresentAtRuntime("io.vertx.ext.web.Router");
boolean acmeEnabled = config.getOptionalValue("quarkus.tls.*.issuer-reference.acme.enabled", Boolean.class).orElse(false);
if (acmeEnabled && !hasVertxHttp) throw new IllegalStateException("Add the quarkus-vertx-http extension to use Let's Encrypt");

Type guard

boolean letsEncryptUsable() {
    return QuarkusClassLoader.isClassPresentAtRuntime("io.vertx.ext.web.Router");
}

Try / catch

// build-step failure cannot be caught at runtime; guard the build input
try {
    build();
} catch (ConfigurationException e) {
    if (e.getMessage().contains("quarkus-vertx-http")) {
        throw new IllegalStateException("Add quarkus-vertx-http or disable Let's Encrypt config", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: An application enables Let's Encrypt certificate management (quarkus.tls.lets-contact/acme configuration) but does not include the quarkus-vertx-http ( Vert.x HTTP ) extension as a dependency, so QuarkusClassLoader.isClassPresentAtRuntime("io.vertx.ext.web.Router") is false at build time.

Common situations: A non-HTTP (e.g. gRPC-only, or plain TLS without vertx-http) Quarkus app adding TLS registry Let's Encrypt config; removing quarkus-vertx-http during a cleanup and forgetting the ACME config remains; copy-pasting TLS config from a REST app into a background worker.

Related errors


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