quarkusio/quarkus · error · java.lang.RuntimeException

Permission class '%s' has %d constructors, exactly one is al

Error message

Permission class '%s' has %d constructors, exactly one is allowed

What it means

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.

Source

Thrown at extensions/security/deployment/src/main/java/io/quarkus/security/deployment/PermissionSecurityChecks.java:394

                for (List<PermissionKey> keyList : keyLists) {
                    for (PermissionKey key : keyList) {
                        if (!classSignatureToConstructor.containsKey(key.classSignature())) {

                            if (key.permissionChecker != null) {
                                // QuarkusPermission we generated for the @PermissionChecker
                                // won't be in the index and as we generated it, we don't need
                                // to validate it
                                classSignatureToConstructor.put(key.classSignature(),
                                        key.permissionChecker.quarkusPermissionConstructor());
                                permissionCheckers.remove(key.permissionChecker);
                                continue;
                            }

                            // validate permission class
                            final ClassInfo clazz = index.getClassByName(key.clazz.name());
                            Objects.requireNonNull(clazz);
                            if (clazz.constructors().size() != 1) {
                                throw new RuntimeException(
                                        String.format("Permission class '%s' has %d constructors, exactly one is allowed",
                                                key.classSignature(), clazz.constructors().size()));
                            }
                            var constructor = clazz.constructors().get(0);
                            // first constructor parameter must be permission name
                            if (constructor.parametersCount() == 0 || !STRING.equals(constructor.parameterType(0).name())) {
                                throw new RuntimeException(
                                        String.format("Permission constructor '%s' first argument must be '%s'",
                                                clazz.name().toString(), String.class.getName()));
                            }
                            // rest of validation needs to be done for computed classes only and per each secured method
                            // therefore we do it later

                            // cache validation result
                            classSignatureToConstructor.put(key.classSignature(), constructor);
                        }
                    }
                }

View on GitHub (pinned to e1c734241f)

Solutions

  1. Keep exactly one constructor on the permission class
  2. Make the single constructor take the permission name (String) as first parameter
  3. Remove extra overloaded constructors or delete Lombok annotations generating multiple constructors

Example fix

// before
public class MyPerm implements Permission {
    public MyPerm() {...}
    public MyPerm(String name, Book b) {...}
}

// after
public class MyPerm implements Permission {
    public MyPerm(String name, Book b) {...}
}
Defensive patterns

Strategy: validation

Validate before calling

Constructor<?>[] ctors = MyPerm.class.getConstructors();
if (ctors.length != 1) throw new IllegalStateException("Permission class must declare exactly one constructor, found " + ctors.length);

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/45c486bfac32a95f. Report an issue: GitHub.