quarkusio/quarkus · error · ProcessingException

Expected a ClientInvocationBuilder

Error message

Expected a ClientInvocationBuilder

What it means

MultiRxInvokerImpl is Quarkus' reactive invoker for RESTEasy Client and only works with RESTEasy's concrete ClientInvocationBuilder, which exposes the internals needed for streaming a Multi. The constructor type-checks the SyncInvoker argument and throws this ProcessingException if it is any other SyncInvoker implementation (e.g. a plain Jersey-style or wrapper invoker). It indicates the reactive client extension is being used with an incompatible client stack.

Solutions

  1. Ensure you are using the RESTEasy Classic client from Quarkus (rest-client / resteasy-client-jaxrs), not another JAX-RS client implementation.
  2. Call the invoker through the standard API: client.invocation(...).rx(MultiRxInvokerImpl.class) on a WebTarget built by ResteasyClient so the SyncInvoker is a ClientInvocationBuilder.
  3. If you must construct it manually, pass builder from ResteasyClient.target(...).request() castable to ClientInvocationBuilder, or check instanceof before constructing.

Example fix

// before
MultiRxInvokerImpl invoker = new MultiRxInvokerImpl(someGenericSyncInvoker, exec); // ProcessingException

// after
ResteasyClient client = (ResteasyClient) ClientBuilder.newClient();
ClientInvocationBuilder sync = (ClientInvocationBuilder) client.target(url).request();
MultiRxInvokerImpl invoker = new MultiRxInvokerImpl(sync, exec);
Defensive patterns

Strategy: type-guard

Validate before calling

if (!(syncInvoker instanceof ClientInvocationBuilder)) {
    throw new IllegalStateException("MultiRxInvokerImpl requires a RESTEasy ClientInvocationBuilder; got " + syncInvoker.getClass());
}

Type guard

static boolean supportsMulti(SyncInvoker invoker) {
    return invoker instanceof org.jboss.resteasy.client.jaxrs.internal.ClientInvocationBuilder;
}

Try / catch

try {
    MultiRxInvokerImpl invoker = new MultiRxInvokerImpl(syncInvoker, exec);
} catch (ProcessingException e) {
    if (e.getMessage().contains("Expected a ClientInvocationBuilder")) {
        throw new IllegalStateException("Use the RESTEasy Classic client so rx() receives a ClientInvocationBuilder", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: Constructing new MultiRxInvokerImpl(syncInvoker, executorService) — directly or via client.rx(MultiRxInvokerImpl.class) — when syncInvoker is not an instance of org.jboss.resteasy.client.jaxrs.internal.ClientInvocationBuilder.

Common situations: Using the Quarkus RESTEasy Reactive client extensions (resteasy-mutiny) on the wrong client implementation on the classpath (e.g. standard JAX-RS client from another provider), or calling RxInvoker APIs through a wrapped/proxied invoker.

Related errors


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

Appendix: source

Thrown at extensions/resteasy-classic/resteasy-mutiny-common/runtime/src/main/java/io/quarkus/resteasy/mutiny/common/runtime/MultiRxInvokerImpl.java:32

import jakarta.ws.rs.sse.SseEventSource;

import org.jboss.resteasy.client.jaxrs.internal.ClientInvocationBuilder;
import org.jboss.resteasy.plugins.providers.sse.InboundSseEventImpl;
import org.jboss.resteasy.plugins.providers.sse.client.SseEventSourceImpl;

import io.smallrye.mutiny.Multi;
import io.smallrye.mutiny.subscription.BackPressureStrategy;

public class MultiRxInvokerImpl implements MultiRxInvoker {

    private static Object monitor = new Object();
    private ClientInvocationBuilder syncInvoker;
    private ScheduledExecutorService executorService;
    private BackPressureStrategy backpressureStrategy = BackPressureStrategy.BUFFER;

    public MultiRxInvokerImpl(final SyncInvoker syncInvoker, final ExecutorService executorService) {
        if (!(syncInvoker instanceof ClientInvocationBuilder)) {
            throw new ProcessingException("Expected a ClientInvocationBuilder");
        }
        this.syncInvoker = (ClientInvocationBuilder) syncInvoker;
        if (executorService instanceof ScheduledExecutorService) {
            this.executorService = (ScheduledExecutorService) executorService;
        }
    }

    @Override
    public BackPressureStrategy getBackPressureStrategy() {
        return backpressureStrategy;
    }

    @Override
    public void setBackPressureStrategy(BackPressureStrategy strategy) {
        this.backpressureStrategy = strategy;
    }

    @Override

View on GitHub (pinned to e1c734241f)