{"record":{"id":"45c486bfac32a95f","repo":"quarkusio/quarkus","slug":"permission-class-s-has-d-constructors-exactly","errorCode":null,"errorMessage":"Permission class '%s' has %d constructors, exactly one is allowed","messagePattern":"Permission class '(.+?)' has (.+?) constructors, exactly one is allowed","errorType":"exception","errorClass":"java.lang.RuntimeException","httpStatus":null,"severity":"error","filePath":"extensions/security/deployment/src/main/java/io/quarkus/security/deployment/PermissionSecurityChecks.java","lineNumber":394,"sourceCode":"                for (List<PermissionKey> keyList : keyLists) {\n                    for (PermissionKey key : keyList) {\n                        if (!classSignatureToConstructor.containsKey(key.classSignature())) {\n\n                            if (key.permissionChecker != null) {\n                                // QuarkusPermission we generated for the @PermissionChecker\n                                // won't be in the index and as we generated it, we don't need\n                                // to validate it\n                                classSignatureToConstructor.put(key.classSignature(),\n                                        key.permissionChecker.quarkusPermissionConstructor());\n                                permissionCheckers.remove(key.permissionChecker);\n                                continue;\n                            }\n\n                            // validate permission class\n                            final ClassInfo clazz = index.getClassByName(key.clazz.name());\n                            Objects.requireNonNull(clazz);\n                            if (clazz.constructors().size() != 1) {\n                                throw new RuntimeException(\n                                        String.format(\"Permission class '%s' has %d constructors, exactly one is allowed\",\n                                                key.classSignature(), clazz.constructors().size()));\n                            }\n                            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                }","sourceCodeStart":376,"sourceCodeEnd":412,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/extensions/security/deployment/src/main/java/io/quarkus/security/deployment/PermissionSecurityChecks.java#L376-L412","documentation":"When a custom Permission class referenced by @PermissionsAllowed is validated, Quarkus requires exactly one constructor because it must programmatically instantiate the permission (passing the permission name plus secured method parameters). A class with zero or multiple constructors cannot be wired unambiguously and fails the build.","triggerScenarios":"A custom permission class used in @PermissionsAllowed (e.g. @PermissionsAllowed(value=\"read\", action=MyPerm.class)) declares more than one constructor (plus the implicit default counts too) and is found by validatePermissionClasses during build.","commonSituations":"Adding a convenience overloaded constructor to an existing permission class; forgetting that the no-arg default constructor counts when another is added; Lombok @AllArgsConstructor plus default constructor.","solutions":["Keep exactly one constructor on the permission class","Make the single constructor take the permission name (String) as first parameter","Remove extra overloaded constructors or delete Lombok annotations generating multiple constructors"],"exampleFix":"// before\npublic class MyPerm implements Permission {\n    public MyPerm() {...}\n    public MyPerm(String name, Book b) {...}\n}\n\n// after\npublic class MyPerm implements Permission {\n    public MyPerm(String name, Book b) {...}\n}","handlingStrategy":"validation","validationCode":"Constructor<?>[] ctors = MyPerm.class.getConstructors();\nif (ctors.length != 1) throw new IllegalStateException(\"Permission class must declare exactly one constructor, found \" + ctors.length);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Give permission classes a single explicit constructor","Avoid Lombok annotations that generate extra constructors on permission classes","Document the one-constructor contract in the team's security guidelines"],"tags":["quarkus","security","build-time","constructor","permission-class"],"backgroundTag":"invalid-permission-class","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"}