quarkusio/quarkus · error · IllegalArgumentException

Specifying executor service is not supported. The underlying

Error message

Specifying executor service is not supported. The underlying call is non-blocking, there is no reason to offload the call to a separate thread pool.

What it means

The MicroProfile REST Client spec allows executorService(ExecutorService) to offload blocking calls, but Quarkus' reactive rest client is fully non-blocking (Vert.x event loop based), so there is nothing to offload. RestClientBuilderImpl.executorService therefore unconditionally throws IllegalArgumentException. It is a deliberate, always-failing API for this implementation.

Source

Thrown at extensions/resteasy-reactive/rest-client/runtime/src/main/java/io/quarkus/rest/client/reactive/runtime/RestClientBuilderImpl.java:362

    @Override
    public RestClientBuilderImpl httpClientOptionsCustomizer(Consumer<HttpClientOptions> httpClientOptionsCustomizer) {
        return clientOptionsCustomizer(httpClientOptionsCustomizer);
    }

    public RestClientBuilderImpl clientRequestCustomizer(Consumer<HttpClientRequest> clientRequestCustomizer) {
        clientBuilder.clientRequestCustomizer(clientRequestCustomizer);
        return this;
    }

    @Override
    public RestClientBuilderImpl httpClientRequestCustomizer(Consumer<HttpClientRequest> httpClientOptionsCustomizer) {
        return clientRequestCustomizer(httpClientOptionsCustomizer);
    }

    @Override
    public RestClientBuilderImpl executorService(ExecutorService executor) {
        throw new IllegalArgumentException("Specifying executor service is not supported. " +
                "The underlying call is non-blocking, " +
                "there is no reason to offload the call to a separate thread pool.");
    }

    @Override
    public Configuration getConfiguration() {
        return clientBuilder.getConfiguration();
    }

    @Override
    public RestClientBuilderImpl property(String name, Object value) {
        clientBuilder.property(name, value);
        return this;
    }

    @Override
    public RestClientBuilderImpl register(Class<?> componentClass) {
        Object bean = BeanGrabber.getBeanIfDefined(componentClass);

View on GitHub (pinned to e1c734241f)

Solutions

  1. Remove the executorService(...) call entirely — the reactive client never blocks.
  2. If thread offloading is truly required, wrap the reactive call with @Blocking on a service layer or use Mutiny's runSubscriptionOn instead.
  3. Guard shared code so executorService is only invoked on non-Quarkus builders.

Example fix

// before
builder.executorService(myExecutor);

// after
// removed: Quarkus reactive client is non-blocking; no executor needed
builder.build(MyClient.class);
Defensive patterns

Strategy: type-guard

Validate before calling

if (builder instanceof io.quarkus.rest.client.reactive.QuarkusRestClientBuilder) {
    // skip executorService call
}

Type guard

boolean supportsExecutorService(RestClientBuilder b) { return !(b instanceof io.quarkus.rest.client.reactive.runtime.RestClientBuilderImpl); }

Try / catch

try {
    builder.executorService(exec);
} catch (IllegalArgumentException e) {
    // Quarkus reactive client: non-blocking, executor unsupported — ignore
}

Prevention

When it happens

Trigger: Calling builder.executorService(anyExecutorService) on a QuarkusRestClientBuilder (usually in code shared between the MP REST Client implementation and Quarkus).

Common situations: Porting existing MicroProfile REST Client code from another vendor (e.g. a Jersey-based stack) to Quarkus; generic builder helper utilities that set executor services speculatively.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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