quarkusio/quarkus · error · java.lang.RuntimeException

@PermissionsAllowed annotation placed on the

Error message

@PermissionsAllowed annotation placed on the '%s' has inclusive relation between its permissions.
                                            The '%s' permission has been matched with @PermissionChecker '%s', therefore you must also define
                                            a @PermissionChecker for '%s' permissions.
                                            

What it means

When @PermissionsAllowed uses inclusive permission relations and one permission in the group is backed by a @PermissionChecker method, Quarkus requires every permission in that group to have a matching @PermissionChecker. If a permission (e.g. 'delete') matched a checker but a sibling permission in the same inclusive annotation has none, deployment fails with this message naming the annotation target, matched permission, existing checker, and the missing permission set.

Solutions

  1. Define a @PermissionChecker method for every permission listed in the inclusive @PermissionsAllowed annotation, e.g. add a method annotated @PermissionChecker("missing-permission") returning PermissionChecker in a CDI bean.
  2. Set inclusive=false on the annotation if an OR/one-of relation (each permission evaluated independently) is actually intended.
  3. Rename the permission in @PermissionsAllowed so it no longer accidentally matches a checker, if plain string permissions are wanted.

Example fix

// before
@PermissionsAllowed(value = {"read", "delete"}, inclusive = true) // 'read' has a checker, 'delete' does not

// after — add to a CDI bean:
@PermissionChecker("delete")
PermissionChecker deleteChecker() { return ctx -> ctx.getSubject().map(...).orElse(false); }
Defensive patterns

Strategy: validation

Validate before calling

// Convention: maintain a single @PermissionChecker per permission name in one config class,
// and a test that every permission listed in inclusive @PermissionsAllowed annotations has a checker.
// grep your sources: every @PermissionsAllowed(value={...}, inclusive=true) name must have @PermissionChecker(name)

Prevention

When it happens

Trigger: Using @PermissionsAllowed(value={...}, inclusive=true) where permission 'a' resolves to a @PermissionChecker method but sibling permission 'b' in the same annotation resolves to a plain StringPermission (no checker).

Common situations: Mixing @PermissionChecker-annotated producer methods with plain string permissions in one inclusive annotation; adding a new permission to an existing inclusive group without adding its checker; typos so a permission name no longer matches any checker method.

Related errors


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

Appendix: source

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

                for (PermissionNameAndChecker checkerPermission : checkerPermissions) {
                    String permissionName = PermissionToActionUtil.parse(checkerPermission.permissionName).name();
                    for (var e : permissionToActions.entrySet()) {
                        PermissionNameAndChecker permissionNameKey = e.getKey();
                        // look for permission names that match our permission checker value (before action-to-perm separator)
                        // for example: read:it
                        if (permissionNameKey.checker == null && permissionNameKey.permissionName.equals(permissionName)) {
                            boolean hasActions = e.getValue() != null && !e.getValue().isEmpty();
                            final String permissionsJoinedWithActions;
                            if (hasActions) {
                                permissionsJoinedWithActions = e.getValue()
                                        .stream()
                                        .map(action -> permissionNameKey.permissionName + PERMISSION_TO_ACTION_SEPARATOR
                                                + action)
                                        .collect(Collectors.joining(", "));
                            } else {
                                permissionsJoinedWithActions = permissionNameKey.permissionName;
                            }
                            throw new RuntimeException(
                                    """
                                            @PermissionsAllowed annotation placed on the '%s' has inclusive relation between its permissions.
                                            The '%s' permission has been matched with @PermissionChecker '%s', therefore you must also define
                                            a @PermissionChecker for '%s' permissions.
                                            """
                                            .formatted(toString(annotationTarget), permissionName,
                                                    toString(checkerPermission.checker.checkerMethod),
                                                    permissionsJoinedWithActions));
                        }
                    }
                }
            }

            for (var permissionToAction : permissionToActions.entrySet().stream().sorted(PERMISSION_TO_ACTION_COMPARATOR)
                    .toList()) {
                final var permissionNameKey = permissionToAction.getKey();
                final var permissionActions = permissionToAction.getValue();
                final var key = new PermissionKey(permissionNameKey.permissionName, permissionActions, params, classType,

View on GitHub (pinned to e1c734241f)