quarkusio/quarkus · error · java.lang.IllegalArgumentException

Detected two @PermissionChecker annotations with same value

Error message

Detected two @PermissionChecker annotations with same value '%s', annotated methods are:
                            - %s
                            - %s
                            

What it means

Two methods were annotated with @PermissionChecker using the same 'value' permission name. Quarkus registers one generated Permission class per checker, keyed by permission name, so duplicates are ambiguous and rejected at build time.

Source

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

                            "@PermissionChecker annotation placed on the '%s' attribute 'value' must not be blank"
                                    .formatted(toString(checkerMethod)));
                }
                boolean isBlocking = checkerMethod.hasDeclaredAnnotation(BLOCKING);
                if (isBlocking && isReactive) {
                    throw new IllegalArgumentException("""
                            @PermissionChecker annotation instance placed on the '%s' returns 'Uni<Boolean>' and is
                            annotated with the @Blocking annotation; if you need to block, please return 'boolean'
                            """.formatted(toString(checkerMethod)));
                }

                var generatedPermissionClassName = getGeneratedPermissionName(checkerMethod, permissionCheckerIndex++);
                var methodParamMappers = new MethodParameterMapper[checkerMethod.parametersCount()];
                var generatedPermissionConstructor = getGeneratedPermissionConstructor(checkerMethod, methodParamMappers);
                var checkerMetadata = new PermissionCheckerMetadata(checkerMethod, generatedPermissionClassName,
                        isReactive, generatedPermissionConstructor, methodParamMappers, isBlocking);

                if (permissionCheckers.containsKey(permissionName)) {
                    throw new IllegalArgumentException("""
                            Detected two @PermissionChecker annotations with same value '%s', annotated methods are:
                            - %s
                            - %s
                            """
                            .formatted(annotationInstance.value().asString(), toString(checkerMethod),
                                    toString(permissionCheckers.get(permissionName).checkerMethod())));
                }

                permissionCheckers.put(permissionName, checkerMetadata);
            }
            return permissionCheckers;
        }

        private static boolean isUniBoolean(MethodInfo checkerMethod) {
            if (checkerMethod.returnType().kind() == Type.Kind.PARAMETERIZED_TYPE) {
                var parametrizedType = checkerMethod.returnType().asParameterizedType();
                boolean returnsUni = UNI.equals(parametrizedType.name());
                boolean booleanArg = parametrizedType.arguments().size() == 1

View on GitHub (pinned to e1c734241f)

Solutions

  1. Give each @PermissionChecker a unique 'value'
  2. Delete the redundant checker method if two methods implement the same check
  3. If both are needed for different resources, encode the distinction in the name, e.g. 'book:read' vs 'author:read'

Example fix

// before
@PermissionChecker("can-read") boolean canReadBook(Book b) {...}
@PermissionChecker("can-read") boolean canReadAuthor(Author a) {...}

// after
@PermissionChecker("book:read") boolean canReadBook(Book b) {...}
@PermissionChecker("author:read") boolean canReadAuthor(Author a) {...}
Defensive patterns

Strategy: validation

Validate before calling

// collect values at startup-test time
Set<String> seen = new HashSet<>();
for (Method m : MyBeans.class.getDeclaredMethods()) {
    PermissionChecker pc = m.getAnnotation(PermissionChecker.class);
    if (pc != null && !seen.add(pc.value())) throw new IllegalStateException("Duplicate @PermissionChecker value: " + pc.value());
}

Prevention

When it happens

Trigger: Declaring @PermissionChecker("can-read") on two different methods (in the same or different beans); both are discovered by the Jandex index during the security deployment step.

Common situations: Duplicating a checker method in a subclass or copied bean without renaming the permission; renaming a method but not its permission value; merging branches that both added the same permission name.

Related errors


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