quarkusio/quarkus · error · IllegalArgumentException

@ExactlyOnce on method ${methodName} does not support Kotlin

Error message

@ExactlyOnce on method ${methodName} does not support Kotlin suspend functions

What it means

Kotlin suspend functions compile to methods taking a Continuation parameter and return an opaque Object/Coroutine return type; the exactly-once invoker and its transactional wrapper are not implemented against Kotlin coroutines, so the extension rejects suspend functions at build time.

Source

Thrown at extensions/smallrye-reactive-messaging-kafka/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/kafka/deployment/SmallRyeReactiveMessagingKafkaProcessor.java:209

            }

            if (method.hasAnnotation(DotNames.WITH_TRANSACTION) && !reactive) {
                throw new IllegalArgumentException(
                        "@ExactlyOnce on method " + methodName
                                + " cannot combine @WithTransaction with a synchronous return type"
                                + ", use @Transactional instead");
            }

            if (method.hasAnnotation(DotNames.TRANSACTIONAL) && method.hasAnnotation(DotNames.WITH_TRANSACTION)) {
                throw new IllegalArgumentException(
                        "@ExactlyOnce on method " + methodName
                                + " cannot combine @Transactional with @WithTransaction");
            }

            boolean isSuspend = method.parameterTypes().stream()
                    .anyMatch(t -> t.name().equals(DotNames.CONTINUATION));
            if (isSuspend) {
                throw new IllegalArgumentException(
                        "@ExactlyOnce on method " + methodName
                                + " does not support Kotlin suspend functions");
            }

            String incomingChannel = incoming.value().asString();
            String outgoingChannel = outgoing.value().asString();

            if (!discoveryState.isKafkaConnector(channelsManagedByConnectors, true, incomingChannel)) {
                throw new IllegalArgumentException(
                        "@ExactlyOnce on method " + methodName
                                + ": incoming channel '" + incomingChannel + "' is not managed by the Kafka connector");
            }
            if (!discoveryState.isKafkaConnector(channelsManagedByConnectors, false, outgoingChannel)) {
                throw new IllegalArgumentException(
                        "@ExactlyOnce on method " + methodName
                                + ": outgoing channel '" + outgoingChannel + "' is not managed by the Kafka connector");
            }

View on GitHub (pinned to e1c734241f)

Solutions

  1. Remove the `suspend` modifier and return Uni/Multi (or a plain type) instead; use kotlinx-coroutines-reactor/mutiny bridges (e.g. uni.await) inside the non-suspend method.
  2. Handle coroutine work inside the method body rather than making the signature suspend.
  3. Drop @ExactlyOnce if suspend semantics are essential.

Example fix

// before
@ExactlyOnce
@Incoming("in") @Outgoing("out")
suspend fun process(p: String): String { ... }

// after
@ExactlyOnce
@Incoming("in") @Outgoing("out")
fun process(p: String): Uni<String> = uni { ... } // bridge coroutines internally
Defensive patterns

Strategy: validation

Validate before calling

// Kotlin: do not declare suspend on @ExactlyOnce functions
// (compile-time check in JVM terms: Continuation must not appear in parameter types)
require(!method.parameters.any { it.type == Continuation::class }) { "suspend not supported with @ExactlyOnce" }

Prevention

When it happens

Trigger: An @ExactlyOnce method written in Kotlin is declared as `suspend fun`, detected via a Continuation parameter in the method's parameter types.

Common situations: Kotlin developers writing suspend processors as they would for plain reactive messaging, then adding @ExactlyOnce for transactional semantics.

Related errors


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/667812a37de616bc. Report an issue: GitHub.