quarkusio/quarkus · error · DeploymentException
${exceptionMsgSupplier.get()} (nonBlocking/virtual-thread an
Error message
${exceptionMsgSupplier.get()} (nonBlocking/virtual-thread annotations conflict) What it means
A gRPC method declares both @NonBlocking and @RunOnVirtualThread. @RunOnVirtualThread already implies a non-blocking, virtual-thread execution model, so combining it with @NonBlocking is contradictory and rejected in nonInheritedBlockingMode.
Source
Thrown at extensions/grpc/deployment/src/main/java/io/quarkus/grpc/deployment/GrpcServerProcessor.java:501
IMPLICIT(true);
final boolean blocking;
BlockingMode(boolean blocking) {
this.blocking = blocking;
}
}
private static BlockingMode nonInheritedBlockingMode(Predicate<DotName> checker,
Supplier<String> exceptionMsgSupplier) {
boolean blocking = checker.test(BLOCKING);
boolean nonBlocking = checker.test(NON_BLOCKING);
boolean vt = checker.test(RUN_ON_VIRTUAL_THREAD);
if (blocking && nonBlocking) {
throw new DeploymentException(exceptionMsgSupplier.get());
}
if (nonBlocking && vt) {
throw new DeploymentException(exceptionMsgSupplier.get());
}
if (blocking && !vt) {
return BlockingMode.BLOCKING;
}
if (vt) {
return BlockingMode.VIRTUAL_THREAD;
}
if (nonBlocking) {
return BlockingMode.NON_BLOCKING;
}
boolean transactional = checker.test(TRANSACTIONAL);
if (transactional) { // Cannot be on a virtual thread here.
return BlockingMode.IMPLICIT;
}
return BlockingMode.UNDEFINED;
}
/**View on GitHub (pinned to e1c734241f)
Solutions
- Remove @NonBlocking and keep only @RunOnVirtualThread
- Or remove @RunOnVirtualThread if classic @NonBlocking worker-thread execution is desired
- Verify superclass/interface annotations are not contributing the duplicate
Example fix
// before
@NonBlocking
@RunOnVirtualThread
public void greet(...) { ... }
// after
@RunOnVirtualThread
public void greet(...) { ... } Defensive patterns
Strategy: validation
Validate before calling
if (m.isAnnotationPresent(NonBlocking.class) && m.isAnnotationPresent(RunOnVirtualThread.class)) {
throw new IllegalStateException("@RunOnVirtualThread already implies non-blocking: " + m);
} Prevention
- Remember @RunOnVirtualThread subsumes @NonBlocking
- When migrating to virtual threads, remove the old @NonBlocking
- Check class-level annotations too, not just method-level
When it happens
Trigger: @NonBlocking and @RunOnVirtualThread are both found by the checker on the same method (directly or inherited), when getMethodBlockingMode resolves the method's blocking mode.
Common situations: Migrating methods from @NonBlocking worker threads to virtual threads and keeping the old annotation; documentation examples mixing both; class-level @NonBlocking plus method-level @RunOnVirtualThread.
Related errors
- ${exceptionMsgSupplier.get()} (blocking annotations conflict
- Cannot attach headers to a null client
- Unsupported client type " + client.getClass()
- RuntimeException(e)
- RuntimeException(e)
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/20c055b13c7233eb.
Report an issue: GitHub.