quarkusio/quarkus · error · IllegalArgumentException
@ExactlyOnce on method ${methodName} must return a value to
Error message
@ExactlyOnce on method ${methodName} must return a value to produce to the outgoing channel What it means
This build-time validation in the Quarkus Kafka extension fires when a method annotated with @ExactlyOnce declares a void (or boxed Void) return type. @ExactlyOnce processing works by writing the produced value to the outgoing Kafka channel inside the same Kafka transaction as the incoming record offset commit, so the method must actually return a value to produce. A void-returning method cannot participate in the exactly-once transactional pipeline, so the deployment fails with this error.
Source
Thrown at extensions/smallrye-reactive-messaging-kafka/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/kafka/deployment/SmallRyeReactiveMessagingKafkaProcessor.java:169
"@ExactlyOnce on method " + methodName + " requires both @Incoming and @Outgoing annotations");
}
if (method.parametersCount() == 0) {
throw new IllegalArgumentException(
"@ExactlyOnce on method " + methodName + " requires at least one parameter");
}
for (Type paramType : method.parameterTypes()) {
if (paramType.name().equals(DotNames.MESSAGE)) {
throw new IllegalArgumentException(
"@ExactlyOnce on method " + methodName
+ " does not support Message parameters, use payload types instead");
}
}
if (method.returnType().name().equals(DotNames.VOID)
|| method.returnType().name().equals(VOID_BOXED)) {
throw new IllegalArgumentException(
"@ExactlyOnce on method " + methodName
+ " must return a value to produce to the outgoing channel");
}
if (method.hasAnnotation(DotNames.BLOCKING) || method.hasAnnotation(DotNames.SMALLRYE_BLOCKING)) {
throw new IllegalArgumentException(
"@ExactlyOnce on method " + methodName
+ " cannot be combined with @Blocking");
}
DotName returnTypeName = method.returnType().name();
boolean reactive = DotNames.UNI.equals(returnTypeName) || DotNames.MULTI.equals(returnTypeName)
|| DotNames.COMPLETION_STAGE.equals(returnTypeName);
if (reactive && method.returnType().kind() == Type.Kind.PARAMETERIZED_TYPE) {
DotName typeArg = method.returnType().asParameterizedType().arguments().get(0).name();
if (typeArg.equals(VOID_BOXED)) {
throw new IllegalArgumentException(View on GitHub (pinned to e1c734241f)
Solutions
- Change the method to return the payload (or Message) that should be sent to the outgoing channel.
- If the method does not need to produce anything, remove @ExactlyOnce and use plain @Transactional / manual acknowledgement instead.
- Switch the outgoing channel semantics: if you only need at-least-once, drop @ExactlyOnce.
Example fix
// before
@Incoming("in")
@Outgoing("out")
@ExactlyOnce
public void process(String payload) { ... }
// after
@Incoming("in")
@Outgoing("out")
@ExactlyOnce
public String process(String payload) { return payload.toUpperCase(); } Defensive patterns
Strategy: validation
Validate before calling
// build-time check: ensure the @ExactlyOnce method returns a non-void type
if (void.class.equals(method.getReturnType()) || Void.class.equals(method.getReturnType())) {
throw new IllegalStateException("@ExactlyOnce method " + method.getName() + " must return a value");
} Prevention
- Always return the produced payload or Message from @ExactlyOnce methods
- Never annotate consumer-only (void) methods with @ExactlyOnce
- Run the Quarkus build early; this is caught at deployment time
When it happens
Trigger: Annotating a @Incoming/@Outgoing connector method with @ExactlyOnce whose return type is void, or Uni<Void>/CompletionStage<Void> (the reactive variant is checked in error 2172).
Common situations: Developers copy a standard @Incoming processor that only consumes (void return) and add @ExactlyOnce to get transactional semantics; or they refactor a method to @ExactlyOnce without realizing the outgoing channel requires a produced value.
Related errors
- @ExactlyOnce on method ${methodName} requires both @Incoming
- @ExactlyOnce on method ${methodName} cannot be combined with
- @ExactlyOnce on method ${methodName} cannot combine @WithTra
- @ExactlyOnce on method ${methodName} cannot combine @Transac
- @ExactlyOnce on method ${methodName} does not support Kotlin
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/b330f7e83c196320.
Report an issue: GitHub.