quarkusio/quarkus · error · java.lang.IllegalArgumentException

@PermissionChecker '%s' matches permission '%s' and actions

Error message

@PermissionChecker '%s' matches permission '%s' and actions '%s' on secured method '%s', but
                                the @PermissionsAllowed instance specified custom permission '%s'. Both cannot be supported.
                                Please choose one.
                                

What it means

A @PermissionsAllowed entry can be satisfied either by a @PermissionChecker method or by a custom Permission implementation — not both. When a permission name (and actions) matches an existing @PermissionChecker but the annotation also specifies a non-default custom permission class, Quarkus throws this IllegalArgumentException in the PermissionKey constructor, naming the checker, the secured target, and the custom class.

Source

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

        }

        private static final class PermissionKey {

            private final String name;
            private final Set<String> actions;
            private final String[] params;
            private final String[] paramsRemainder;
            private final Type clazz;
            private final boolean inclusive;
            private final PermissionCheckerMetadata permissionChecker;

            private PermissionKey(String name, Set<String> actions, String[] params, Type clazz, boolean inclusive,
                    PermissionCheckerMetadata permissionChecker, AnnotationTarget permsAllowedTarget) {
                this.permissionChecker = permissionChecker;
                this.name = name;
                if (permissionChecker != null) {
                    if (isNotDefaultStringPermission(clazz)) {
                        throw new IllegalArgumentException("""
                                @PermissionChecker '%s' matches permission '%s' and actions '%s' on secured method '%s', but
                                the @PermissionsAllowed instance specified custom permission '%s'. Both cannot be supported.
                                Please choose one.
                                """.formatted(PermissionSecurityChecksBuilder.toString(permissionChecker.checkerMethod()), name,
                                actions, PermissionSecurityChecksBuilder.toString(permsAllowedTarget), clazz.name()));
                    }
                    this.clazz = Type.create(DotName.createSimple(permissionChecker.generatedClassName()), Type.Kind.CLASS);
                } else {
                    this.clazz = clazz;
                }
                this.inclusive = inclusive;
                if (!actions.isEmpty()) {
                    this.actions = actions;
                } else {
                    this.actions = null;
                }

                if (params == null || params.length == 0) {

View on GitHub (pinned to e1c734241f)

Solutions

  1. Choose one mechanism: remove the clazz attribute to use the @PermissionChecker, or remove/rename the @PermissionChecker method to keep the custom Permission class.
  2. Rename the custom permission so it no longer matches the checker's permission name if both permissions must coexist.
  3. Keep actions consistent — the collision is triggered by matched name+actions; adjusting actions alone does not fix it, resolve the overlap explicitly.

Example fix

// before (both defined)
@PermissionChecker("get")
PermissionChecker getChecker() { ... }
@PermissionsAllowed(value = "get", clazz = GetPermission.class)

// after (keep custom permission, drop checker)
@PermissionsAllowed(value = "get", clazz = GetPermission.class)
Defensive patterns

Strategy: validation

Validate before calling

// Before adding a @PermissionChecker, check no @PermissionsAllowed with clazz= uses the same name:
// grep -rn "clazz\s*=" src/main/java | while read l; do
//   name=$(extract value); grep -rn "@PermissionChecker(\"$name\")" src/main/java && echo "conflict: $l"
// done

Prevention

When it happens

Trigger: @PermissionsAllowed(value="p", clazz=MyPermission.class) where a @PermissionChecker("p") method exists (with matching actions), so the name-based checker lookup collides with the explicit custom class.

Common situations: Adding a @PermissionChecker for a permission already secured with a custom Permission class; renaming a custom permission so it now collides with an existing checker name; team members independently wiring both mechanisms for the same permission.

Related errors


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