{"record":{"id":"20c055b13c7233eb","repo":"quarkusio/quarkus","slug":"exceptionmsgsupplier-get-nonblocking-virtual","errorCode":null,"errorMessage":"${exceptionMsgSupplier.get()} (nonBlocking/virtual-thread annotations conflict)","messagePattern":"(.+?) \\(nonBlocking/virtual-thread annotations conflict\\)","errorType":"exception","errorClass":"DeploymentException","httpStatus":null,"severity":"error","filePath":"extensions/grpc/deployment/src/main/java/io/quarkus/grpc/deployment/GrpcServerProcessor.java","lineNumber":501,"sourceCode":"        IMPLICIT(true);\n\n        final boolean blocking;\n\n        BlockingMode(boolean blocking) {\n            this.blocking = blocking;\n        }\n    }\n\n    private static BlockingMode nonInheritedBlockingMode(Predicate<DotName> checker,\n            Supplier<String> exceptionMsgSupplier) {\n        boolean blocking = checker.test(BLOCKING);\n        boolean nonBlocking = checker.test(NON_BLOCKING);\n        boolean vt = checker.test(RUN_ON_VIRTUAL_THREAD);\n        if (blocking && nonBlocking) {\n            throw new DeploymentException(exceptionMsgSupplier.get());\n        }\n        if (nonBlocking && vt) {\n            throw new DeploymentException(exceptionMsgSupplier.get());\n        }\n        if (blocking && !vt) {\n            return BlockingMode.BLOCKING;\n        }\n        if (vt) {\n            return BlockingMode.VIRTUAL_THREAD;\n        }\n        if (nonBlocking) {\n            return BlockingMode.NON_BLOCKING;\n        }\n        boolean transactional = checker.test(TRANSACTIONAL);\n        if (transactional) { // Cannot be on a virtual thread here.\n            return BlockingMode.IMPLICIT;\n        }\n        return BlockingMode.UNDEFINED;\n    }\n\n    /**","sourceCodeStart":483,"sourceCodeEnd":519,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/extensions/grpc/deployment/src/main/java/io/quarkus/grpc/deployment/GrpcServerProcessor.java#L483-L519","documentation":"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.","triggerScenarios":"@NonBlocking and @RunOnVirtualThread are both found by the checker on the same method (directly or inherited), when getMethodBlockingMode resolves the method's blocking mode.","commonSituations":"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.","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"],"exampleFix":"// before\n@NonBlocking\n@RunOnVirtualThread\npublic void greet(...) { ... }\n\n// after\n@RunOnVirtualThread\npublic void greet(...) { ... }","handlingStrategy":"validation","validationCode":"if (m.isAnnotationPresent(NonBlocking.class) && m.isAnnotationPresent(RunOnVirtualThread.class)) {\n    throw new IllegalStateException(\"@RunOnVirtualThread already implies non-blocking: \" + m);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Remember @RunOnVirtualThread subsumes @NonBlocking","When migrating to virtual threads, remove the old @NonBlocking","Check class-level annotations too, not just method-level"],"tags":["grpc","virtual-threads","annotation-conflict"],"backgroundTag":"annotation-conflict","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}