{"record":{"id":"89fee9afba7ff904","repo":"quarkusio/quarkus","slug":"found-permissionchecker-annotation-instances-that","errorCode":null,"errorMessage":"Found @PermissionChecker annotation instances that authorize the '%s' permissions, however\n                            no @PermissionsAllowed annotation instance requires these permissions\n                            ","messagePattern":"Found @PermissionChecker annotation instances that authorize the '(.+?)' permissions, however\n                            no @PermissionsAllowed annotation instance requires these permissions\n                            ","errorType":"exception","errorClass":"java.lang.RuntimeException","httpStatus":null,"severity":"error","filePath":"extensions/security/deployment/src/main/java/io/quarkus/security/deployment/PermissionSecurityChecks.java","lineNumber":416,"sourceCode":"                            var constructor = clazz.constructors().get(0);\n                            // first constructor parameter must be permission name\n                            if (constructor.parametersCount() == 0 || !STRING.equals(constructor.parameterType(0).name())) {\n                                throw new RuntimeException(\n                                        String.format(\"Permission constructor '%s' first argument must be '%s'\",\n                                                clazz.name().toString(), String.class.getName()));\n                            }\n                            // rest of validation needs to be done for computed classes only and per each secured method\n                            // therefore we do it later\n\n                            // cache validation result\n                            classSignatureToConstructor.put(key.classSignature(), constructor);\n                        }\n                    }\n                }\n            }\n            if (!permissionCheckers.isEmpty()) {\n                if (permissionCheckers.size() > 1) {\n                    throw new RuntimeException(\"\"\"\n                            Found @PermissionChecker annotation instances that authorize the '%s' permissions, however\n                            no @PermissionsAllowed annotation instance requires these permissions\n                            \"\"\".formatted(String.join(\",\", permissionCheckers.values())));\n                } else {\n                    throw new RuntimeException(\"\"\"\n                            Found @PermissionChecker annotation instance that authorize the '%s' permission, however\n                            no @PermissionsAllowed annotation instance requires this permission\n                            \"\"\".formatted(permissionCheckers.values().iterator().next()));\n                }\n            }\n            return this;\n        }\n\n        PermissionSecurityChecksBuilder gatherPermissionsAllowedAnnotations(\n                Map<MethodInfo, AnnotationInstance> alreadyCheckedMethods,\n                Map<ClassInfo, AnnotationInstance> alreadyCheckedClasses,\n                List<AnnotationInstance> additionalClassInstances,\n                Predicate<MethodInfo> hasAdditionalSecurityAnnotations) {","sourceCodeStart":398,"sourceCodeEnd":434,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/extensions/security/deployment/src/main/java/io/quarkus/security/deployment/PermissionSecurityChecks.java#L398-L434","documentation":"One or more @PermissionChecker methods were registered, but no method in the application requires the permissions they authorize via @PermissionsAllowed. Unused permission checkers indicate a wiring mistake (or dead code) and are rejected at build time; this variant fires when more than one checker is unmatched.","triggerScenarios":"Declaring two or more @PermissionChecker methods whose permission values never appear in any @PermissionsAllowed annotation on endpoints/methods — detected in validatePermissionClasses after gathering all required permission keys.","commonSituations":"Removing/refactoring the secured endpoints (or their @PermissionsAllowed) while leaving the checkers; typos in permission names so the sets don't intersect; checkers in a library module whose consumers never use them.","solutions":["Add @PermissionsAllowed(\"<name>\") on the resource methods that should require each checker's permission","Fix typos so the permission name matches exactly between checker and requirement","Remove the now-unused @PermissionChecker methods"],"exampleFix":"// before\n@PermissionChecker(\"book:read\") boolean canRead(...) {...}\n// (no @PermissionsAllowed anywhere)\n\n// after\n@PermissionChecker(\"book:read\") boolean canRead(...) {...}\n// in the resource:\n@PermissionsAllowed(\"book:read\")\npublic Book get(Long id) {...}","handlingStrategy":"validation","validationCode":"// every checker value must appear in some @PermissionsAllowed\nSet<String> required = scanForPermissionsAllowedValues();\nSet<String> provided = scanForPermissionCheckerValues();\nSet<String> unmatched = new HashSet<>(provided); unmatched.removeAll(required);\nif (unmatched.size() > 1) throw new IllegalStateException(\"Unmatched checkers: \" + unmatched);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Add @PermissionsAllowed in the same commit as new @PermissionChecker methods","Keep checkers and their secured endpoints in the same module","Grep for permission-name typos in CI"],"tags":["quarkus","security","build-time","permissions-allowed","wiring"],"backgroundTag":"unused-permission-checker","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}