quarkusio/quarkus · error · RuntimeException
Only one of the '<annotations>' annotations can be applied o
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.
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)
Solutions
- Keep only one auth mechanism annotation on the class and delete the other
- If both behaviors are needed, use configuration-based policy (quarkus.http.auth.permission) instead
- 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
- Pick one auth mechanism per endpoint class
- Remember @CodeFlow and @Tenant are the only combinable ones
- Prefer quarkus.http.auth.permission config rules for layered policies
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
- @PermissionChecker annotation placed on the '%s' attribute '
- The @AuthorizationPolicy annotation placed on '<target>' mus
- Security annotations '<annotations>' cannot be applied on th
- Only one of the '<annotations>' annotations can be applied o
- The configuration ${clazz} is missing the @ConfigRoot annota
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/bd78969f9dfb6bb0.
Report an issue: GitHub.