quarkusio/quarkus · error · IllegalStateException
Suspendable @Blocking methods are not supported yet
Error message
Suspendable @Blocking methods are not supported yet: %s.%s
What it means
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.
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
Example fix
// before
@Blocking
@GET
suspend fun list(): List<Item> = repo.findAll()
// after
@GET
suspend fun list(): List<Item> = withContext(Dispatchers.IO) { repo.findAll() } Defensive patterns
Strategy: validation
Validate before calling
// Build-time / code-review guard
if (method.isSuspend && method.hasAnnotation(Blocking::class)) {
error("@Blocking is not allowed on suspend functions")
} Type guard
fun MethodInfo.isSuspendAndBlocking(): Boolean =
this.annotation(BLOCKING_ANNOTATION) != null && parameters.any { it.name() == null } == false // check suspend flag in Jandex Prevention
- 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
When it happens
Trigger: Declaring a JAX-RS resource method in Kotlin as `suspend fun` and annotating it with io.smallrye.common.annotation.Blocking.
Common situations: Developers assuming they need @Blocking because the coroutine runs on a worker thread, or copy-pasting @Blocking annotations from Java endpoints onto suspend functions.
Related errors
- No Vertx context found
- No Vertx context found
- Not a coroutine invoker
- Something went wrong during parameter type resolution -…
- Suspend method ' of class ' ' cannot be used as a request…
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/0ddb37bf3fdbcc3f.
Report an issue: GitHub.
Appendix: source
Thrown at extensions/resteasy-reactive/rest-kotlin/deployment/src/main/java/io/quarkus/resteasy/reactive/kotlin/deployment/KotlinCoroutineIntegrationProcessor.java:92
recorder));
if (methodContext.containsKey(EndpointIndexer.METHOD_CONTEXT_CUSTOM_RETURN_TYPE_KEY)) {
Type methodReturnType = (Type) methodContext.get(EndpointIndexer.METHOD_CONTEXT_CUSTOM_RETURN_TYPE_KEY);
if (methodReturnType != null) {
if (methodReturnType.name().equals(FLOW)) {
return List.of(processor, flowCustomizer());
}
}
}
return Collections.singletonList(processor);
}
return Collections.emptyList();
}
private void ensureNotBlocking(MethodInfo method) {
if (method.annotation(BLOCKING_ANNOTATION) != null) {
String format = String.format("Suspendable @Blocking methods are not supported yet: %s.%s",
method.declaringClass().name(), method.name());
throw new IllegalStateException(format);
}
}
@Override
public ParameterExtractor handleCustomParameter(Type paramType, Map<DotName, AnnotationInstance> annotations,
boolean field, Map<String, Object> methodContext) {
//look for methods that take a Continuation, these are suspendable and need to be handled differently
if (paramType.name().equals(CONTINUATION)) {
methodContext.put(NAME, true);
if (paramType.kind() == Type.Kind.PARAMETERIZED_TYPE) {
Type firstGenericType = paramType.asParameterizedType().arguments().get(0);
if (firstGenericType.kind() == Type.Kind.WILDCARD_TYPE) {
methodContext.put(EndpointIndexer.METHOD_CONTEXT_CUSTOM_RETURN_TYPE_KEY,
firstGenericType.asWildcardType().superBound());
}
}
return new NullParamExtractor();View on GitHub (pinned to e1c734241f)