{"record":{"id":"89e7edd5bf526e9a","repo":"quarkusio/quarkus","slug":"static-method-cannot-be-annotated-with-the-per","errorCode":null,"errorMessage":"Static method '' cannot be annotated with the @PermissionChecker annotation","messagePattern":"Static method '' cannot be annotated with the @PermissionChecker annotation","errorType":"exception","errorClass":"java.lang.RuntimeException","httpStatus":null,"severity":"error","filePath":"extensions/security/deployment/src/main/java/io/quarkus/security/deployment/PermissionSecurityChecks.java","lineNumber":138,"sourceCode":"            // and produce permission augmenter can and did in past run concurrently\n            this.permissionInstances = Collections.unmodifiableList(instances);\n            this.permissionNameToChecker = Collections.unmodifiableMap(getPermissionCheckers(index));\n        }\n\n        private static Map<String, PermissionCheckerMetadata> getPermissionCheckers(IndexView index) {\n            int permissionCheckerIndex = 0; // this ensures generated QuarkusPermission name is unique\n            var permissionCheckers = new HashMap<String, PermissionCheckerMetadata>();\n            for (var annotationInstance : index.getAnnotations(PERMISSION_CHECKER_NAME)) {\n                var checkerMethod = annotationInstance.target().asMethod();\n                if (Modifier.isPrivate(checkerMethod.flags())) {\n                    // we generate QuarkusPermission in the same package as where the @PermissionChecker is detected\n                    // so the checker method must be either public or package-private\n                    throw new RuntimeException(\"Private method '\" + toString(checkerMethod)\n                            + \"' cannot be annotated with the @PermissionChecker annotation\");\n                }\n                if (Modifier.isStatic(checkerMethod.flags())) {\n                    // checkers must be CDI bean member methods for now, so the checker method must not be static\n                    throw new RuntimeException(\"Static method '\" + toString(checkerMethod)\n                            + \"' cannot be annotated with the @PermissionChecker annotation\");\n                }\n                boolean isReactive = isUniBoolean(checkerMethod);\n                if (!isReactive && !isPrimitiveBoolean(checkerMethod)) {\n                    throw new RuntimeException((\"@PermissionChecker method '%s' has return type '%s', but only \" +\n                            \"supported return types are 'boolean' and 'Uni<Boolean>'. \")\n                            .formatted(toString(checkerMethod), checkerMethod.returnType().name()));\n                }\n\n                var permissionName = annotationInstance.value().asString();\n                if (permissionName.isBlank()) {\n                    throw new IllegalArgumentException(\n                            \"@PermissionChecker annotation placed on the '%s' attribute 'value' must not be blank\"\n                                    .formatted(toString(checkerMethod)));\n                }\n                boolean isBlocking = checkerMethod.hasDeclaredAnnotation(BLOCKING);\n                if (isBlocking && isReactive) {\n                    throw new IllegalArgumentException(\"\"\"","sourceCodeStart":120,"sourceCodeEnd":156,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/extensions/security/deployment/src/main/java/io/quarkus/security/deployment/PermissionSecurityChecks.java#L120-L156","documentation":"@PermissionChecker methods must be instance members of a CDI bean because the security runtime invokes them via the bean. Static methods cannot be proxied/called as bean members, so the deployment rejects them at build time.","triggerScenarios":"Annotating a static method with @PermissionChecker and building the application; the build step getPermissionCheckers scans the Jandex index and throws.","commonSituations":"Developer writes a stateless utility-style checker as a static method (a common Java idiom) inside a bean or plain class, e.g. static boolean isOwner(...) annotated with @PermissionChecker, and the build fails.","solutions":["Remove the 'static' modifier so the method is an instance member of a CDI bean","Ensure the enclosing class is a CDI bean (e.g. @ApplicationScoped) so the checker can be invoked","If logic is shared statically, keep the static helper and have a non-static @PermissionChecker method call it"],"exampleFix":"// before\n@PermissionChecker(\"can-view\")\nstatic boolean canView(Identity id) { return id.hasRole(\"viewer\"); }\n// after\n@ApplicationScoped\nclass SecurityChecks {\n    @PermissionChecker(\"can-view\")\n    boolean canView(Identity id) { return id.hasRole(\"viewer\"); }\n}","handlingStrategy":"validation","validationCode":"for (Method m : SecurityChecks.class.getDeclaredMethods()) {\n    if (m.isAnnotationPresent(PermissionChecker.class) && Modifier.isStatic(m.getModifiers())) {\n        throw new IllegalStateException(\"@PermissionChecker method must not be static: \" + m.getName());\n    }\n}","typeGuard":"boolean isInstanceChecker(java.lang.reflect.Method m) {\n    return m.isAnnotationPresent(PermissionChecker.class) && !Modifier.isStatic(m.getModifiers());\n}","tryCatchPattern":"try {\n    appBootstrap();\n} catch (RuntimeException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"Static method\") && e.getMessage().contains(\"@PermissionChecker\")) {\n        throw new IllegalStateException(\"Convert static @PermissionChecker to an instance method on a CDI bean\", e);\n    }\n    throw e;\n}","preventionTips":["Never mark @PermissionChecker methods static","Enclose checkers in @ApplicationScoped CDI beans","Delegate to static helpers instead of annotating them","Include security bean conventions in code review checklists"],"tags":["security","cdi","build-time","annotation-validation"],"backgroundTag":"invalid-annotation-target","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}