quarkusio/quarkus · error · IllegalStateException

The execution model %s of %s is not supported

Error message

The execution model %s of %s is not supported

What it means

DefaultBlockingReceiverExecutor only runs receivers whose execution model it supports (blocking models). At runtime, execute() checks the receiver's execution model and throws IllegalStateException if it cannot run it — the build-time check (error 2111) was bypassed, e.g. by a custom Receiver registered programmatically.

Source

Thrown at extensions/signals/runtime/src/main/java/io/quarkus/signals/runtime/impl/DefaultBlockingReceiverExecutor.java:40

    private final ConcurrencyLimiter blockingLimiter;

    DefaultBlockingReceiverExecutor(ExecutorService executorService, SignalsRuntimeConfig config) {
        this.executorService = executorService;
        int limit = config.receivers().blockingConcurrencyLimit().orElse(-1);
        this.blockingLimiter = limit > 0 ? new ConcurrencyLimiter(limit) : null;
    }

    @Override
    public boolean supportsExecutionModel(ExecutionModel val) {
        return val == ExecutionModel.BLOCKING;
    }

    @Override
    public <SIGNAL, RESPONSE> Uni<RESPONSE> execute(Receiver<SIGNAL, RESPONSE> receiver, SignalContext<SIGNAL> context) {
        ExecutionModel executionModel = receiver.executionModel();
        if (!supportsExecutionModel(executionModel)) {
            throw new IllegalStateException(
                    "The execution model %s of %s is not supported".formatted(executionModel, receiver));
        }
        LOG.debugf("Notify %s [signal=%s, emission=%s]", receiver, context.signalType(),
                context.emissionType());
        CompletableFuture<RESPONSE> ret = execute(executionModel, new Callable<Uni<RESPONSE>>() {
            @Override
            public Uni<RESPONSE> call() throws Exception {
                return receiver.notify(context);
            }
        });
        return Uni.createFrom().completionStage(ret);
    }

    protected <RESULT> CompletableFuture<RESULT> execute(ExecutionModel executionModel, Callable<Uni<RESULT>> action) {
        CompletableFuture<RESULT> ret = new CompletableFuture<>();
        ConcurrencyLimiter limiter = blockingLimiter;
        if (limiter != null) {
            limiter.run(new Runnable() {

View on GitHub (pinned to e1c734241f)

Solutions

  1. Change the receiver to a blocking signature/model supported by the default executor.
  2. Register/configure an executor implementation that supports the receiver's execution model.
  3. Verify custom receivers are validated with supportsExecutionModel() before wiring them.

Example fix

// before
receivers.register(new Receiver<>() {
  public Uni<Response> onSignal(Signal s) { ... }
});

// after (blocking executor)
receivers.register(new Receiver<>() {
  public Response onSignal(Signal s) { ... }
});
Defensive patterns

Strategy: try-catch

Validate before calling

// Before wiring a custom receiver:
if (!executor.supportsExecutionModel(receiver.executionModel())) { throw new IllegalArgumentException("executor cannot run " + receiver); }

Try / catch

try {
    executor.execute(receiver, ctx).await().indefinitely();
} catch (IllegalStateException e) {
    if (e.getMessage().contains("execution model")) { log.fatal("receiver executor/model mismatch"); throw e; }
    throw e;
}

Prevention

When it happens

Trigger: Calling execute() on a receiver whose Receiver.executionModel() is a non-blocking/reactive model while using DefaultBlockingReceiverExecutor, typically via a programmatically registered receiver.

Common situations: Registering a custom receiver through Receivers API with an async signature but the default (blocking) executor selected, or config switching executors after receivers were written reactively.

Related errors


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