quarkusio/quarkus · error · java.lang.RuntimeException
@PermissionsAllowed annotation placed on the '%s' has inclus
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.
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)
Solutions
- 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.
- Set inclusive=false on the annotation if an OR/one-of relation (each permission evaluated independently) is actually intended.
- 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
- For inclusive=true groups, define a @PermissionChecker for every listed permission before writing the annotation.
- Prefer inclusive=false (default OR semantics) unless all-permissions-required is truly needed.
- Centralize permission names as constants to avoid typos that break checker matching.
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
- @PermissionChecker '%s' matches permission '%s' and actions
- @PermissionsAllowed annotation placed on method '%s' has 'pa
- @PermissionChecker declared on method '%s', but no matching
- Found @PermissionChecker annotation instance declared on the
- Cannot transform exception ${exception}
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/1335087759f96165.
Report an issue: GitHub.