quarkusio/quarkus · error · IllegalArgumentException

@ExactlyOnce on method ${methodName}: incoming channel '${in

Error message

@ExactlyOnce on method ${methodName}: incoming channel '${incomingChannel}' is not managed by the Kafka connector

What it means

@ExactlyOnce requires both the incoming and outgoing channel to be Kafka-connector-managed, because exactly-once is implemented with Kafka consumer/producer transactions. This error reports that the incoming channel is served by a different connector (or in-memory/manual channels), so the extension cannot set up the transactional pipeline.

Source

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

            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");
            }

            LOGGER.infof("Exactly-once processing detected on method %s#%s, " +
                    "configuring channels '%s' (incoming) and '%s' (outgoing)",
                    method.declaringClass().name(), method.name(), incomingChannel, outgoingChannel);

            // Auto-configure outgoing channel for transactions
            produceRuntimeConfigurationDefaultBuildItem(discoveryState, defaultConfigProducer,
                    getChannelOutgoingPropertyName(outgoingChannel, "transactional.id"),
                    "${quarkus.application.name}-" + outgoingChannel);
            produceRuntimeConfigurationDefaultBuildItem(discoveryState, defaultConfigProducer,

View on GitHub (pinned to e1c734241f)

Solutions

  1. Set mp.messaging.incoming.<channel>.connector=smallrye-kafka (or remove an overriding connector value).
  2. Correct the channel name in @Incoming so it matches the Kafka-configured channel.
  3. Remove @ExactlyOnce if either channel is intentionally non-Kafka.

Example fix

// before
mp.messaging.incoming.in.connector=smallrye-amqp

// after
mp.messaging.incoming.in.connector=smallrye-kafka
mp.messaging.incoming.in.topic=my-topic
Defensive patterns

Strategy: validation

Validate before calling

// verify incoming channel connector before deploying
String connector = config.getValue("mp.messaging.incoming." + channel + ".connector", String.class);
if (!"smallrye-kafka".equals(connector)) {
    throw new IllegalStateException("incoming channel must use smallrye-kafka for @ExactlyOnce");
}

Prevention

When it happens

Trigger: The @Incoming channel name passed to discoveryState.isKafkaConnector(channelsManagedByConnectors, true, channel) is not registered as a Kafka connector channel at build time — e.g. it's an in-memory channel or another connector (Pulsar, AMQP).

Common situations: Typos in channel names, configuring the channel only via application.properties with a wrong connector type, or mixing an in-memory test channel with @ExactlyOnce.

Related errors


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