quarkusio/quarkus · error · java.lang.RuntimeException

@PermissionChecker method '%s' declares checked exceptions w

Error message

@PermissionChecker method '%s' declares checked exceptions which is not allowed

What it means

A @PermissionChecker method declares thrown checked exceptions. Quarkus generates a Permission constructor mirroring the checker's parameters and cannot propagate checked exceptions through it, so checked exceptions on permission checker methods are disallowed at build time.

Source

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

            if (checkerMethod.returnType().kind() == Type.Kind.PARAMETERIZED_TYPE) {
                var parametrizedType = checkerMethod.returnType().asParameterizedType();
                boolean returnsUni = UNI.equals(parametrizedType.name());
                boolean booleanArg = parametrizedType.arguments().size() == 1
                        && BOOLEAN.equals(parametrizedType.arguments().get(0).name());
                return returnsUni && booleanArg;
            }
            return false;
        }

        private static boolean isPrimitiveBoolean(MethodInfo checkerMethod) {
            return checkerMethod.returnType().kind() == Type.Kind.PRIMITIVE
                    && Primitive.BOOLEAN.equals(checkerMethod.returnType().asPrimitiveType().primitive());
        }

        private static MethodInfo getGeneratedPermissionConstructor(MethodInfo checkerMethod,
                MethodParameterMapper[] paramMappers) {
            if (!checkerMethod.exceptions().isEmpty()) {
                throw new RuntimeException("@PermissionChecker method '%s' declares checked exceptions which is not allowed"
                        .formatted(toString(checkerMethod)));
            }

            // Permission constructor: permission name, <<secured-method-parameters>>...
            // Permission checker method: [optionally at any place SecurityIdentity], <<secured-method-parameters>>...
            // that is constructor param length great or equal to checker method param length
            int constructorParameterCount = checkerMethod.parametersCount() + (hasSecurityIdentityParam(checkerMethod) ? 0 : 1);
            final Type[] constructorParameterTypes = new Type[constructorParameterCount];
            final String[] constructorParameterNames = new String[constructorParameterCount];

            constructorParameterNames[0] = "permissionName";
            constructorParameterTypes[0] = Type.create(String.class);

            for (int i = 0, j = 1; i < checkerMethod.parametersCount(); i++) {
                var parameterType = checkerMethod.parameterType(i);
                if (SECURITY_IDENTITY_NAME.equals(parameterType.name())) {
                    paramMappers[i] = new MethodParameterMapper(i, MethodParameterMapper.SECURITY_IDENTITY_IDX);
                } else {

View on GitHub (pinned to e1c734241f)

Solutions

  1. Remove the 'throws' clause and handle the exception inside the method (return false or wrap)
  2. Catch the checked exception inside the checker and translate it to a boolean/Uni<Boolean> result
  3. Move the throwing logic out of the checker into a helper that throws a RuntimeException

Example fix

// before
@PermissionChecker("book:read")
boolean canRead(Book b) throws SQLException { ... }

// after
@PermissionChecker("book:read")
boolean canRead(Book b) {
    try { ...; return true; } catch (SQLException e) { throw new RuntimeException(e); }
}
Defensive patterns

Strategy: validation

Validate before calling

for (Class<?> ex : MyBean.class.getMethod("canRead").getExceptionTypes()) {
    if (!RuntimeException.class.isAssignableFrom(ex) && !Error.class.isAssignableFrom(ex))
        throw new IllegalStateException("@PermissionChecker must not declare checked exception: " + ex);
}

Prevention

When it happens

Trigger: Declaring 'throws SomeCheckedException' (or extending a class that does) on a method annotated with @PermissionChecker, discovered via MethodInfo.exceptions() during build.

Common situations: Checker methods calling IO/DAO code that throws checked exceptions (SQLException, IOException); wrapping legacy service methods as permission checkers without adapting their signature.

Related errors


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