quarkusio/quarkus · error · IllegalStateException
Currently suspend methods for Reactive Messaging are not sup
Error message
Currently suspend methods for Reactive Messaging are not supported on methods that are only annotated with @Outgoing
What it means
Kotlin suspend functions are supported as Reactive Messaging mediators, but not when the method is only @Outgoing (a publisher). The invoker generation step throws IllegalStateException because the TODO'd publisher-side coroutine support is unimplemented. @Incoming (or incoming+outgoing) suspend methods are fine.
Source
Thrown at extensions/smallrye-reactive-messaging/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/deployment/SmallRyeReactiveMessagingProcessor.java:443
baseName = DotNames.simpleName(bean.getImplClazz().enclosingClass()) + "_"
+ DotNames.simpleName(bean.getImplClazz().name());
} else {
baseName = DotNames.simpleName(bean.getImplClazz().name());
}
StringBuilder sigBuilder = new StringBuilder();
sigBuilder.append(method.name()).append("_").append(method.returnType().name().toString());
for (Type i : method.parameterTypes()) {
sigBuilder.append(i.name().toString());
}
String targetPackage = DotNames.internalPackageNameWithTrailingSlash(bean.getImplClazz().name());
String generatedName = targetPackage + baseName
+ INVOKER_SUFFIX + "_" + method.name() + "_"
+ HashUtil.sha1(sigBuilder.toString());
if (isSuspendMethod
&& ((mediatorConfiguration.getIncoming().isEmpty()) && (mediatorConfiguration.getOutgoing() != null))) {
// TODO: this restriction needs to be lifted
throw new IllegalStateException(
"Currently suspend methods for Reactive Messaging are not supported on methods that are only annotated with @Outgoing");
}
if (!isSuspendMethod) {
generateStandardInvoker(method, gizmo, generatedName);
} else if (!mediatorConfiguration.getIncoming().isEmpty()) {
generateSubscribingCoroutineInvoker(method, gizmo, generatedName);
}
return generatedName.replace('/', '.');
}
private void generateStandardInvoker(MethodInfo method, Gizmo gizmo, String generatedName) {
String dotName = generatedName.replace('/', '.');
gizmo.class_(dotName, cc -> {
cc.implements_(Invoker.class);
String beanInstanceType = method.declaringClass().name().toString();View on GitHub (pinned to e1c734241f)
Solutions
- Remove `suspend` from the @Outgoing-only method and return a reactive type (Multi/Uni/Publisher) instead.
- Do the async work inside a coroutineScope launched from a non-suspend method, returning the resulting Multi/Uni.
- Convert the method to include an @Incoming stream so suspend is supported.
Example fix
// before
@Outgoing("out")
suspend fun produce(): String = compute()
// after
@Outgoing("out")
fun produce(): Uni<String> = Uni.createFrom().item { runBlocking { compute() } } Defensive patterns
Strategy: validation
Validate before calling
// Ensure suspend methods always have an @Incoming:
fun isValidSuspendMediator(hasIncoming: Boolean, hasOutgoing: Boolean, isSuspend: Boolean): Boolean =
!isSuspend || hasIncoming Prevention
- Only mark mediators with an @Incoming stream as suspend.
- For producers, use Multi/Uni return types instead of suspend.
- Track SmallRye Reactive Messaging releases — this TODO restriction may be lifted.
When it happens
Trigger: Declaring `suspend fun` combined with only @Outgoing; the deployment then generates the invoker and hits this check in generatedInvokerName/generateInvoker.
Common situations: Kotlin users writing periodic producers with @Outgoing as suspend functions after seeing suspend+@Incoming examples work.
Related errors
- Method ${class}.${method} is suspendable but kotlinx-corouti
- Suspendable rest client method{jandexMethod} is present on c
- Something went wrong during parameter type resolution - expe
- No Vertx context found
- No Vertx context found. Consider using @NonBlocking on the c
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/6ee1688ea9abbf37.
Report an issue: GitHub.