quarkusio/quarkus · error · ConfigurationException

The method `${method}` is using `@RunOnVirtualThread` but ex

Error message

The method `${method}` is using `@RunOnVirtualThread` but explicitly set as `@Blocking(ordered = true)`

What it means

Quarkus Reactive Messaging validates mediator method configuration at build time. A method annotated with @RunOnVirtualThread must not simultaneously be declared @Blocking(ordered = true), because ordered blocking forces a serialized worker pool that conflicts with virtual thread execution semantics. The build fails with a ConfigurationException.

Source

Thrown at extensions/smallrye-reactive-messaging/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/deployment/QuarkusMediatorConfigurationUtil.java:248

            }
        }

        AnnotationInstance blockingAnnotation = methodInfo.annotation(BLOCKING);
        AnnotationInstance smallryeBlockingAnnotation = methodInfo.annotation(SMALLRYE_BLOCKING);
        AnnotationInstance transactionalAnnotation = methodInfo.annotation(TRANSACTIONAL);
        AnnotationInstance runOnVirtualThreadAnnotation = methodInfo.annotation(RUN_ON_VIRTUAL_THREAD);
        // IF @RunOnVirtualThread is used on the declaring class, it forces all @Blocking method to be run on virtual threads.
        AnnotationInstance runOnVirtualThreadClassAnnotation = methodInfo.declaringClass()
                .declaredAnnotation(RUN_ON_VIRTUAL_THREAD);
        if (blockingAnnotation != null || smallryeBlockingAnnotation != null || transactionalAnnotation != null
                || runOnVirtualThreadAnnotation != null) {
            mediatorConfigurationSupport.validateBlocking(validationOutput);
            configuration.setBlocking(true);
            if (blockingAnnotation != null) {
                AnnotationValue ordered = blockingAnnotation.value("ordered");
                if (runOnVirtualThreadAnnotation != null || runOnVirtualThreadClassAnnotation != null) {
                    if (ordered != null && ordered.asBoolean()) {
                        throw new ConfigurationException(
                                "The method `" + methodInfo.name()
                                        + "` is using `@RunOnVirtualThread` but explicitly set as `@Blocking(ordered = true)`");
                    }
                    configuration.setBlockingExecutionOrdered(false);
                    configuration.setWorkerPoolName(QuarkusWorkerPoolRegistry.DEFAULT_VIRTUAL_THREAD_WORKER);
                } else {
                    configuration.setBlockingExecutionOrdered(ordered == null || ordered.asBoolean());
                }
                String poolName;
                if (blockingAnnotation.value() != null &&
                        !(poolName = blockingAnnotation.value().asString()).equals(Blocking.DEFAULT_WORKER_POOL)) {
                    configuration.setWorkerPoolName(poolName);
                }
            } else if (runOnVirtualThreadAnnotation != null || runOnVirtualThreadClassAnnotation != null) {
                configuration.setBlockingExecutionOrdered(false);
                configuration.setWorkerPoolName(QuarkusWorkerPoolRegistry.DEFAULT_VIRTUAL_THREAD_WORKER);
            } else {
                configuration.setBlockingExecutionOrdered(true);

View on GitHub (pinned to e1c734241f)

Solutions

  1. Remove @Blocking(ordered = true) — keep @Blocking (unordered) if blocking execution is needed; Quarkus will run it on the virtual thread worker
  2. Drop @RunOnVirtualThread if you explicitly need ordered blocking on the default worker pool
  3. Use plain @Blocking (no ordered attribute) when combining with @RunOnVirtualThread

Example fix

// before
@Incoming("in")
@RunOnVirtualThread
@Blocking(ordered = true)
void process(String msg) { ... }
// after
@Incoming("in")
@RunOnVirtualThread
@Blocking
void process(String msg) { ... }
Defensive patterns

Strategy: validation

Validate before calling

// at build/test time, assert the combination is absent
if (method.hasAnnotation(DotNames.RUN_ON_VIRTUAL_THREAD)
    && method.hasAnnotation(DotNames.BLOCKING)
    && method.annotation(DotNames.BLOCKING).value("ordered") != null
    && method.annotation(DotNames.BLOCKING).value("ordered").asBoolean()) {
    throw new IllegalStateException("@RunOnVirtualThread + @Blocking(ordered=true)");
}

Prevention

When it happens

Trigger: Combining @Incoming (or @Outgoing) processing with both @RunOnVirtualThread and @Blocking(ordered = true) on the same method; inheriting @RunOnVirtualThread from the class while adding ordered @Blocking on the method.

Common situations: Migrating to virtual threads and keeping the old ordered-blocking annotation; copy-pasting @Blocking(ordered = true) from another mediator method.

Related errors


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