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
- Remove the 'throws' clause and handle the exception inside the method (return false or wrap)
- Catch the checked exception inside the checker and translate it to a boolean/Uni<Boolean> result
- 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
- Never add 'throws' clauses to permission checker methods
- Catch and translate checked exceptions inside the checker body
- Wrap throwing DAOs in non-throwing service helpers
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
- @PermissionChecker annotation placed on the '%s' attribute '
- @PermissionChecker annotation instance placed on the '%s' re
- Detected two @PermissionChecker annotations with same value
- Permission class '%s' has %d constructors, exactly one is al
- Permission constructor '%s' first argument must be '%s'
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/7ff8969b0859cdd4.
Report an issue: GitHub.