{"record":{"id":"7c4191ad9bed101f","repo":"quarkusio/quarkus","slug":"permissionchecker-declared-on-method-s-but-no","errorCode":null,"errorMessage":"@PermissionChecker declared on method '%s', but no matching CDI bean could be found for the declaring class '%s'.","messagePattern":"@PermissionChecker declared on method '(.+?)', but no matching CDI bean could be found for the declaring class '(.+?)'\\.","errorType":"exception","errorClass":"java.lang.RuntimeException","httpStatus":null,"severity":"error","filePath":"extensions/security/deployment/src/main/java/io/quarkus/security/deployment/SecurityProcessor.java","lineNumber":878,"sourceCode":"            // - this processor relies on the bean archive index (cycle: idx -> additional bean -> idx)\n            // - we have injection points (=> better validation from Arc) as checker beans are only requested from this augmentor\n            var syntheticBeanConfigurator = SyntheticBeanBuildItem\n                    .configure(QuarkusPermissionSecurityIdentityAugmentor.class)\n                    .addType(SecurityIdentityAugmentor.class)\n                    // ATM we do get augmentors from CDI once, no need to keep the instance in the CDI container\n                    .scope(Dependent.class)\n                    .unremovable()\n                    .addInjectionPoint(Type.create(BlockingSecurityExecutor.class))\n                    .createWith(recorder.createPermissionAugmentor());\n\n            checkerBuilder.instance.getPermissionCheckers().stream().forEach(checkerMethod -> {\n                var checkerClassType = Type.create(checkerMethod.declaringClass().name(), Type.Kind.CLASS);\n\n                // validate permission checker method's declaring class is a CDI bean\n                // synthetic beans are not taken into consideration which makes them not supported\n                var matchingBeans = beanDiscoveryFinishedBuildItem.beanStream().assignableTo(checkerClassType).collect();\n                if (matchingBeans.isEmpty()) {\n                    throw new RuntimeException(\n                            \"\"\"\n                                    @PermissionChecker declared on method '%s', but no matching CDI bean could be found for the declaring class '%s'.\n                                    \"\"\"\n                                    .formatted(checkerMethod.name(), checkerClassType.name()));\n                }\n                // Using @Dependent is problematic because we would have to destroy beans manually at some point (which?)\n                matchingBeans.stream().filter(b -> BuiltinScope.DEPENDENT.getInfo().equals(b.getScope())).findFirst()\n                        .ifPresent(bi -> {\n                            throw new RuntimeException(\n                                    \"\"\"\n                                            Found @PermissionChecker annotation instance declared on the CDI bean method '%s#%s'.\n                                            The CDI bean is a dependent scoped bean, but only the '@Singleton' bean or normal scoped beans are supported\n                                            \"\"\"\n                                            .formatted(checkerMethod.name(), checkerClassType.name()));\n                        });\n\n                syntheticBeanConfigurator.addInjectionPoint(checkerClassType);\n            });","sourceCodeStart":860,"sourceCodeEnd":896,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/extensions/security/deployment/src/main/java/io/quarkus/security/deployment/SecurityProcessor.java#L860-L896","documentation":"Methods annotated with @PermissionChecker must live on a class that is a discoverable CDI bean, because Quarkus will obtain the checker by looking up a bean assignable to the declaring class during build. If bean discovery finds no matching bean for the declaring class, this error is thrown. Synthetic beans are explicitly not supported for permission checkers.","triggerScenarios":"Declaring a @PermissionChecker method on a class that is not a CDI bean — missing bean-defining annotation (@ApplicationScoped, @Singleton, etc.), or the class is only registered as a synthetic bean via an extension.","commonSituations":"Forgetting a scope annotation on the checker class; putting @PermissionChecker on a utility/helper class outside bean discovery; checker in a library package excluded from bean discovery; relying on @Unremovable synthetic beans.","solutions":["Add a bean-defining annotation (e.g. @ApplicationScoped or @Singleton) to the class declaring the @PermissionChecker method.","Verify the class is in a bean-discovery archive (has beans.xml or bean-defining annotations).","If the class was produced by an extension as a synthetic bean, move the @PermissionChecker method to a regular CDI bean instead.","Check that the declaring class name matches the actual bean class (no accidental subclass mismatch)."],"exampleFix":"// before\npublic class PaymentChecker {\n    @PermissionChecker(\"pay\")\n    boolean canPay() { ... }\n}\n\n// after\n@ApplicationScoped\npublic class PaymentChecker {\n    @PermissionChecker(\"pay\")\n    boolean canPay() { ... }\n}","handlingStrategy":"validation","validationCode":"// ensure declaring class is a CDI bean before adding @PermissionChecker\nboolean isBean = PaymentChecker.class.isAnnotationPresent(jakarta.enterprise.context.ApplicationScoped.class) || PaymentChecker.class.isAnnotationPresent(jakarta.inject.Singleton.class);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always annotate @PermissionChecker host classes with a scope","Never place @PermissionChecker on synthetic-bean-produced classes","Keep checkers in bean-discovery archives"],"tags":["quarkus","security","cdi","permissionchecker"],"backgroundTag":"cdi-bean-not-found","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"}