{"record":{"id":"68268d712556ebb5","repo":"quarkusio/quarkus","slug":"exactlyonce-on-method-methodname-cannot-combin-68268d","errorCode":null,"errorMessage":"@ExactlyOnce on method ${methodName} cannot combine @Transactional with @WithTransaction","messagePattern":"@ExactlyOnce on method (.+?) cannot combine @Transactional with @WithTransaction","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":201,"sourceCode":"\n            if (reactive && method.returnType().kind() == Type.Kind.PARAMETERIZED_TYPE) {\n                DotName typeArg = method.returnType().asParameterizedType().arguments().get(0).name();\n                if (typeArg.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\n            if (method.hasAnnotation(DotNames.WITH_TRANSACTION) && !reactive) {\n                throw new IllegalArgumentException(\n                        \"@ExactlyOnce on method \" + methodName\n                                + \" cannot combine @WithTransaction with a synchronous return type\"\n                                + \", use @Transactional instead\");\n            }\n\n            if (method.hasAnnotation(DotNames.TRANSACTIONAL) && method.hasAnnotation(DotNames.WITH_TRANSACTION)) {\n                throw new IllegalArgumentException(\n                        \"@ExactlyOnce on method \" + methodName\n                                + \" cannot combine @Transactional with @WithTransaction\");\n            }\n\n            boolean isSuspend = method.parameterTypes().stream()\n                    .anyMatch(t -> t.name().equals(DotNames.CONTINUATION));\n            if (isSuspend) {\n                throw new IllegalArgumentException(\n                        \"@ExactlyOnce on method \" + methodName\n                                + \" does not support Kotlin suspend functions\");\n            }\n\n            String incomingChannel = incoming.value().asString();\n            String outgoingChannel = outgoing.value().asString();\n\n            if (!discoveryState.isKafkaConnector(channelsManagedByConnectors, true, incomingChannel)) {\n                throw new IllegalArgumentException(\n                        \"@ExactlyOnce on method \" + methodName","sourceCodeStart":183,"sourceCodeEnd":219,"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#L183-L219","documentation":"@Transactional (CDI interceptor) and @WithTransaction (reactive messaging annotation) are two mutually exclusive transaction mechanisms; applying both on an @ExactlyOnce method is ambiguous — the extension cannot decide which transaction manages the method. The build-time processor rejects the combination.","triggerScenarios":"A single @ExactlyOnce method carries both @jakarta.transaction.Transactional and @WithTransaction annotations simultaneously.","commonSituations":"Adding @WithTransaction to a method that already had @Transactional (or vice versa) during migration between sync and reactive styles.","solutions":["Keep only @Transactional for synchronous methods.","Keep only @WithTransaction for reactive (Uni/Multi/CompletionStage) methods.","Remove the redundant annotation depending on the intended execution model."],"exampleFix":"// before\n@ExactlyOnce @Transactional @WithTransaction\n@Incoming(\"in\") @Outgoing(\"out\")\npublic Uni<String> process(String p) { ... }\n\n// after\n@ExactlyOnce @WithTransaction\n@Incoming(\"in\") @Outgoing(\"out\")\npublic Uni<String> process(String p) { ... }","handlingStrategy":"validation","validationCode":"// reject double transaction annotations\nif (method.isAnnotationPresent(Transactional.class) && method.isAnnotationPresent(WithTransaction.class)) {\n    throw new IllegalStateException(\"Choose either @Transactional or @WithTransaction\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Audit methods for stacked transaction annotations before adding @ExactlyOnce","Document which transaction annotation is the codebase standard"],"tags":["kafka","reactive-messaging","exactly-once","transactions"],"backgroundTag":"conflicting-transaction-annotations","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"}