{"record":{"id":"0ddb37bf3fdbcc3f","repo":"quarkusio/quarkus","slug":"suspendable-blocking-methods-are-not-supported-ye","errorCode":null,"errorMessage":"Suspendable @Blocking methods are not supported yet: %s.%s","messagePattern":"Suspendable @Blocking methods are not supported yet: (.+?)\\.(.+?)","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"extensions/resteasy-reactive/rest-kotlin/deployment/src/main/java/io/quarkus/resteasy/reactive/kotlin/deployment/KotlinCoroutineIntegrationProcessor.java","lineNumber":92,"sourceCode":"                                    recorder));\n                    if (methodContext.containsKey(EndpointIndexer.METHOD_CONTEXT_CUSTOM_RETURN_TYPE_KEY)) {\n                        Type methodReturnType = (Type) methodContext.get(EndpointIndexer.METHOD_CONTEXT_CUSTOM_RETURN_TYPE_KEY);\n                        if (methodReturnType != null) {\n                            if (methodReturnType.name().equals(FLOW)) {\n                                return List.of(processor, flowCustomizer());\n                            }\n                        }\n                    }\n                    return Collections.singletonList(processor);\n                }\n                return Collections.emptyList();\n            }\n\n            private void ensureNotBlocking(MethodInfo method) {\n                if (method.annotation(BLOCKING_ANNOTATION) != null) {\n                    String format = String.format(\"Suspendable @Blocking methods are not supported yet: %s.%s\",\n                            method.declaringClass().name(), method.name());\n                    throw new IllegalStateException(format);\n                }\n            }\n\n            @Override\n            public ParameterExtractor handleCustomParameter(Type paramType, Map<DotName, AnnotationInstance> annotations,\n                    boolean field, Map<String, Object> methodContext) {\n                //look for methods that take a Continuation, these are suspendable and need to be handled differently\n                if (paramType.name().equals(CONTINUATION)) {\n                    methodContext.put(NAME, true);\n                    if (paramType.kind() == Type.Kind.PARAMETERIZED_TYPE) {\n                        Type firstGenericType = paramType.asParameterizedType().arguments().get(0);\n                        if (firstGenericType.kind() == Type.Kind.WILDCARD_TYPE) {\n                            methodContext.put(EndpointIndexer.METHOD_CONTEXT_CUSTOM_RETURN_TYPE_KEY,\n                                    firstGenericType.asWildcardType().superBound());\n                        }\n\n                    }\n                    return new NullParamExtractor();","sourceCodeStart":74,"sourceCodeEnd":110,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/extensions/resteasy-reactive/rest-kotlin/deployment/src/main/java/io/quarkus/resteasy/reactive/kotlin/deployment/KotlinCoroutineIntegrationProcessor.java#L74-L110","documentation":"KotlinCoroutineIntegrationProcessor scans REST endpoint methods at build time. A Kotlin suspend fun annotated with @Blocking cannot be supported by the coroutine integration, so the deployment fails fast with IllegalStateException. Combining suspension with blocking execution semantics is inherently contradictory in this framework.","triggerScenarios":"Declaring a JAX-RS resource method in Kotlin as `suspend fun` and annotating it with io.smallrye.common.annotation.Blocking.","commonSituations":"Developers assuming they need @Blocking because the coroutine runs on a worker thread, or copy-pasting @Blocking annotations from Java endpoints onto suspend functions.","solutions":["Remove the @Blocking annotation from the suspend method — suspend methods are dispatched on the coroutine dispatcher automatically","If blocking work is required, move it into a non-suspend method annotated with @Blocking and call it via Dispatchers.IO or a separate bean","Restructure the endpoint as a non-suspend method returning CompletionStage/Uni if coroutine support is not needed"],"exampleFix":"// before\n@Blocking\n@GET\nsuspend fun list(): List<Item> = repo.findAll()\n// after\n@GET\nsuspend fun list(): List<Item> = withContext(Dispatchers.IO) { repo.findAll() }","handlingStrategy":"validation","validationCode":"// Build-time / code-review guard\nif (method.isSuspend && method.hasAnnotation(Blocking::class)) {\n    error(\"@Blocking is not allowed on suspend functions\")\n}","typeGuard":"fun MethodInfo.isSuspendAndBlocking(): Boolean =\n    this.annotation(BLOCKING_ANNOTATION) != null && parameters.any { it.name() == null } == false // check suspend flag in Jandex","tryCatchPattern":null,"preventionTips":["Never combine @Blocking with suspend fun in Kotlin endpoints","Use withContext(Dispatchers.IO) for blocking calls inside coroutines","Review Kotlin resource methods for accidental @Blocking annotations"],"tags":["kotlin","coroutines","blocking","build-time","resteasy-reactive"],"backgroundTag":"blocking-suspend-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"}