quarkusio/quarkus · error · RuntimeException

Only one of the ' ' annotations can be applied on the ' '…

Error message

Only one of the '<annotations>' annotations can be applied on the '<class>' class

What it means

A class (endpoint) may only carry one of the mutually exclusive HTTP security annotations such as @Basic, @Form, @BearerAuth, etc. Combining two of these on the same class is ambiguous — only one auth mechanism can be selected per endpoint — so the build fails. @CodeFlow and @Tenant are the exceptions and may be combined with others.

Solutions

  1. Keep only one auth mechanism annotation on the class and delete the other
  2. If both behaviors are needed, use configuration-based policy (quarkus.http.auth.permission) instead
  3. Move the mechanism choice to individual endpoint methods if different endpoints need different mechanisms

Example fix

// before
@Basic
@Form
public class UserResource { ... }
// after
@Basic
public class UserResource { ... }
Defensive patterns

Strategy: validation

Validate before calling

long count = Arrays.stream(Resource.class.getAnnotations())
    .map(Annotation::annotationType)
    .filter(t -> Set.of(Basic.class, Form.class, BearerAuth.class).contains(t))
    .count();
if (count > 1) throw new IllegalStateException("Only one auth mechanism annotation allowed on class");

Prevention

When it happens

Trigger: Annotating the same endpoint class with two different auth mechanism annotations (e.g. both @Basic and @Form), where the binding does not allow repetition.

Common situations: Merging code from branches where different developers chose different mechanisms; copy-pasting security annotations between classes without removing the old one; misunderstanding which annotations are mutually exclusive.

Related errors


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

Appendix: source

Thrown at extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/HttpSecurityProcessor.java:858

                for (AnnotationInstance annotation : annotationInstances) {
                    if (annotation.target().kind() != appliesTo) {
                        continue;
                    }
                    if (annotation.target().kind() == AnnotationTarget.Kind.CLASS) {
                        ClassInfo interceptedClass = annotation.target().asClass();
                        if (interceptedClass.isAnnotation()) {
                            // currently we don't support meta-annotations
                            // this is the easiest way to avoid detecting @HttpAuthenticationMechanism on @BasicAuthentication
                            continue;
                        }

                        if (hasClassLevelSecurity.test(interceptedClass)) {
                            // endpoint can only be annotated with one of @Basic, @Form, ...
                            // however combining @CodeFlow and @Tenant is supported
                            var appliedBindings = cache.computeIfAbsent(interceptedClass, a -> new ArrayList<>());
                            if (appliedBindings.contains(interceptorBinding)) {
                                if (!interceptorBinding.allowToRepeatThisInterceptorBinding()) {
                                    throw new RuntimeException(
                                            "Only one of the '%s' annotations can be applied on the '%s' class".formatted(
                                                    Arrays.toString(interceptorBinding.getAnnotationBindings()),
                                                    interceptedClass));
                                }
                            } else {
                                appliedBindings.add(interceptorBinding);
                            }

                            // don't apply security interceptor on individual methods, but on the class-level instead
                            bindingValueToInterceptedClasses
                                    .computeIfAbsent(interceptorBinding.getBindingValue(annotation, annotationBinding,
                                            interceptedClass), s -> new HashSet<>())
                                    .add(interceptedClass.name().toString());
                            continue;
                        }

                        for (MethodInfo method : interceptedClass.methods()) {
                            if (hasProperEndpointModifiers(method)) {

View on GitHub (pinned to e1c734241f)