quarkusio/quarkus · error · IllegalArgumentException
@ExactlyOnce on method ${methodName} does not support Messag
Error message
@ExactlyOnce on method ${methodName} does not support Message parameters, use payload types instead What it means
With @ExactlyOnce, parameters must be plain payload types, not io.smallrye.reactive.messaging.Message<T>, because the transactional producer path re-wraps payloads itself and cannot round-trip framework Message metadata through the transaction. A Message-typed parameter fails the deployment.
Source
Thrown at extensions/smallrye-reactive-messaging-kafka/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/kafka/deployment/SmallRyeReactiveMessagingKafkaProcessor.java:161
MethodInfo method = annotation.target().asMethod();
String methodName = method.declaringClass().name() + "#" + method.name();
AnnotationInstance incoming = method.annotation(DotNames.INCOMING);
AnnotationInstance outgoing = method.annotation(DotNames.OUTGOING);
if (incoming == null || outgoing == null) {
throw new IllegalArgumentException(
"@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");
}
View on GitHub (pinned to e1c734241f)
Solutions
- Change the parameter (and return) type from Message<T> to the raw payload T.
- If metadata is needed, access it via the incoming metadata on the payload type or restructure to use connector-provided metadata on the payload.
- Drop @ExactlyOnce if Message-level ack/nack control is required.
Example fix
// before
@Incoming("in") @Outgoing("out") @ExactlyOnce
Message<String> process(Message<String> in) { ... }
// after
@Incoming("in") @Outgoing("out") @ExactlyOnce
String process(String payload) { return transform(payload); } Defensive patterns
Strategy: validation
Validate before calling
fun validateExactlyOnceParams(paramTypeNames: List<String>): Boolean =
paramTypeNames.none { it.startsWith("io.smallrye.reactive.messaging.Message") } Prevention
- Use raw payload types (String, Order, etc.) with @ExactlyOnce.
- Reserve Message<T> for at-least-once processors needing metadata/ack control.
- When adding @ExactlyOnce to existing processors, refactor Message parameters first.
When it happens
Trigger: Declaring an @ExactlyOnce processing method like `Message<String> process(Message<String> in)`.
Common situations: Reusing an existing at-least-once processor that used Message for metadata/ack control, then adding @ExactlyOnce.
Related errors
- @ExactlyOnce on method ${methodName} requires both @Incoming
- @ExactlyOnce on method ${methodName} requires at least one p
- Unknown cache type:
- Unable to determine the value type for '" + cacheName + "' R
- Unsupported type: " + type
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/1907696d8aaf0a76.
Report an issue: GitHub.