{"record":{"id":"1907696d8aaf0a76","repo":"quarkusio/quarkus","slug":"exactlyonce-on-method-methodname-does-not-supp","errorCode":null,"errorMessage":"@ExactlyOnce on method ${methodName} does not support Message parameters, use payload types instead","messagePattern":"@ExactlyOnce on method (.+?) does not support Message parameters, use payload types instead","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"extensions/smallrye-reactive-messaging-kafka/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/kafka/deployment/SmallRyeReactiveMessagingKafkaProcessor.java","lineNumber":161,"sourceCode":"            MethodInfo method = annotation.target().asMethod();\n            String methodName = method.declaringClass().name() + \"#\" + method.name();\n\n            AnnotationInstance incoming = method.annotation(DotNames.INCOMING);\n            AnnotationInstance outgoing = method.annotation(DotNames.OUTGOING);\n\n            if (incoming == null || outgoing == null) {\n                throw new IllegalArgumentException(\n                        \"@ExactlyOnce on method \" + methodName + \" requires both @Incoming and @Outgoing annotations\");\n            }\n\n            if (method.parametersCount() == 0) {\n                throw new IllegalArgumentException(\n                        \"@ExactlyOnce on method \" + methodName + \" requires at least one parameter\");\n            }\n\n            for (Type paramType : method.parameterTypes()) {\n                if (paramType.name().equals(DotNames.MESSAGE)) {\n                    throw new IllegalArgumentException(\n                            \"@ExactlyOnce on method \" + methodName\n                                    + \" does not support Message parameters, use payload types instead\");\n                }\n            }\n\n            if (method.returnType().name().equals(DotNames.VOID)\n                    || method.returnType().name().equals(VOID_BOXED)) {\n                throw new IllegalArgumentException(\n                        \"@ExactlyOnce on method \" + methodName\n                                + \" must return a value to produce to the outgoing channel\");\n            }\n\n            if (method.hasAnnotation(DotNames.BLOCKING) || method.hasAnnotation(DotNames.SMALLRYE_BLOCKING)) {\n                throw new IllegalArgumentException(\n                        \"@ExactlyOnce on method \" + methodName\n                                + \" cannot be combined with @Blocking\");\n            }\n","sourceCodeStart":143,"sourceCodeEnd":179,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/extensions/smallrye-reactive-messaging-kafka/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/kafka/deployment/SmallRyeReactiveMessagingKafkaProcessor.java#L143-L179","documentation":"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.","triggerScenarios":"Declaring an @ExactlyOnce processing method like `Message<String> process(Message<String> in)`.","commonSituations":"Reusing an existing at-least-once processor that used Message for metadata/ack control, then adding @ExactlyOnce.","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."],"exampleFix":"// before\n@Incoming(\"in\") @Outgoing(\"out\") @ExactlyOnce\nMessage<String> process(Message<String> in) { ... }\n// after\n@Incoming(\"in\") @Outgoing(\"out\") @ExactlyOnce\nString process(String payload) { return transform(payload); }","handlingStrategy":"validation","validationCode":"fun validateExactlyOnceParams(paramTypeNames: List<String>): Boolean =\n    paramTypeNames.none { it.startsWith(\"io.smallrye.reactive.messaging.Message\") }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["kafka","exactly-once","message","quarkus","deployment"],"backgroundTag":"exactly-once-annotation-misuse","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}