quarkusio/quarkus · error · java.lang.IllegalStateException

Class %s is annotated with multiple security annotations %s

Error message

Class %s is annotated with multiple security annotations %s and %s

What it means

A class was annotated with @PermissionsAllowed and also with another class-level security annotation (e.g. @Authenticated, @RolesAllowed). Combining @PermissionsAllowed with other security annotations at class level is not allowed because it makes authorization ambiguous; Quarkus reports the two conflicting annotation names and fails the build.

Source

Thrown at extensions/security/deployment/src/main/java/io/quarkus/security/deployment/PermissionSecurityChecks.java:497

                                }

                                if (hasAdditionalSecurityAnnotations.test(methodInfo)) {
                                    continue;
                                }

                                // ignore method annotated with other security annotation
                                boolean noMethodLevelSecurityAnnotation = !alreadyCheckedMethods.containsKey(methodInfo);
                                // ignore method annotated with method-level @PermissionsAllowed
                                boolean noMethodLevelPermissionsAllowed = !targetToPermissionKeys.containsKey(methodInfo);
                                if (noMethodLevelSecurityAnnotation && noMethodLevelPermissionsAllowed) {

                                    gatherPermissionKeys(instance, methodInfo, cache, classMethodToPermissionKeys);
                                }
                            }
                        } else {

                            // we do not allow combining @PermissionsAllowed with other security annotations as @Authenticated
                            throw new IllegalStateException(
                                    String.format("Class %s is annotated with multiple security annotations %s and %s", clazz,
                                            instance.name(), existingClassInstance.name()));
                        }
                    }
                }
            }
            targetToPermissionKeys.putAll(classMethodToPermissionKeys);
            for (var instance : additionalClassInstances) {
                gatherPermissionKeys(instance, instance.target(), cache, targetToPermissionKeys);
            }

            // for validation purposes, so that we detect correctly combinations with other security annotations
            var targetInstances = new ArrayList<>(permissionInstances);
            targetInstances.addAll(additionalClassInstances);
            targetToPermissionKeys.keySet().forEach(at -> {
                if (at.kind() == AnnotationTarget.Kind.CLASS) {
                    var classInfo = at.asClass();
                    alreadyCheckedClasses.put(classInfo, getAnnotationInstance(classInfo, targetInstances));

View on GitHub (pinned to e1c734241f)

Solutions

  1. Remove the other class-level security annotation, keeping only @PermissionsAllowed
  2. Convert the other annotation's policy into a @PermissionChecker + permission name used by @PermissionsAllowed
  3. If both policies are genuinely needed, apply them at different levels consistently within one annotation system

Example fix

// before
@Authenticated
@PermissionsAllowed(value="book:read", action=BookPerm.class)
public class BookResource {...}

// after
@PermissionsAllowed(value="book:read", action=BookPerm.class)
public class BookResource {...}
Defensive patterns

Strategy: validation

Validate before calling

for (Annotation a : BookResource.class.getAnnotations()) {
    if (!(a instanceof PermissionsAllowed) && SECURITY_ANNOTATIONS.contains(a.annotationType()))
        throw new IllegalStateException("@PermissionsAllowed must not be combined with " + a.annotationType());
}

Prevention

When it happens

Trigger: Placing @PermissionsAllowed on a class that also carries @Authenticated/@RolesAllowed/@DenyAll etc.; detected in gatherPermissionsAllowedAnnotations when an existingClassInstance for the same class has a different annotation name.

Common situations: Adding @PermissionsAllowed to an already-secured class when tightening authorization; applying annotations from different security generations (legacy RBAC vs the permission-checker API) to the same bean.

Related errors


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