quarkusio/quarkus · error · IllegalArgumentException

@ExactlyOnce on method ${methodName} requires at least one p

Error message

@ExactlyOnce on method ${methodName} requires at least one parameter

What it means

An @ExactlyOnce Kafka processing method must accept at least one parameter, because exactly-once processing needs the incoming payload/Message to forward to the @Outgoing stream. A zero-parameter method fails the deployment build.

Source

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

        DefaultSerdeDiscoveryState discoveryState = new DefaultSerdeDiscoveryState(combinedIndex.getIndex());

        for (AnnotationInstance annotation : combinedIndex.getIndex().getAnnotations(DotNames.EXACTLY_ONCE)) {
            if (annotation.target().kind() != AnnotationTarget.Kind.METHOD) {
                continue;
            }
            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");
            }

View on GitHub (pinned to e1c734241f)

Solutions

  1. Add the incoming payload parameter (e.g. String m or the specific payload type).
  2. Use a Message<T> payload? No — Message parameters are rejected; use raw payload type instead (see related error).
  3. Remove @ExactlyOnce if the method is meant to be a pure producer.

Example fix

// before
@Incoming("in") @Outgoing("out") @ExactlyOnce
String process() { return "x"; }
// after
@Incoming("in") @Outgoing("out") @ExactlyOnce
String process(String payload) { return transform(payload); }
Defensive patterns

Strategy: validation

Validate before calling

fun validateExactlyOnceSignature(paramCount: Int, hasExactlyOnce: Boolean): Boolean =
    !hasExactlyOnce || paramCount >= 1

Prevention

When it happens

Trigger: @ExactlyOnce on a method with no parameters (e.g. a supplier-style method alongside @Incoming+@Outgoing).

Common situations: Converting a @Pollable/supplier method to @ExactlyOnce; cleaning up 'unused' parameters that were actually required.

Related errors


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