quarkusio/quarkus · error · java.lang.IllegalArgumentException
@PermissionChecker annotation instance placed on the '%s' re
Error message
@PermissionChecker annotation instance placed on the '%s' returns 'Uni<Boolean>' and is
annotated with the @Blocking annotation; if you need to block, please return 'boolean' What it means
A @PermissionChecker method returning Uni<Boolean> (reactive check) was also annotated with @Blocking. Reactive permission checkers must not block; if you need to block on an external check you must return plain boolean so Quarkus invokes it on a worker thread. This combination is rejected at build time.
Source
Thrown at extensions/security/deployment/src/main/java/io/quarkus/security/deployment/PermissionSecurityChecks.java:156
throw new RuntimeException("Static method '" + toString(checkerMethod)
+ "' cannot be annotated with the @PermissionChecker annotation");
}
boolean isReactive = isUniBoolean(checkerMethod);
if (!isReactive && !isPrimitiveBoolean(checkerMethod)) {
throw new RuntimeException(("@PermissionChecker method '%s' has return type '%s', but only " +
"supported return types are 'boolean' and 'Uni<Boolean>'. ")
.formatted(toString(checkerMethod), checkerMethod.returnType().name()));
}
var permissionName = annotationInstance.value().asString();
if (permissionName.isBlank()) {
throw new IllegalArgumentException(
"@PermissionChecker annotation placed on the '%s' attribute 'value' must not be blank"
.formatted(toString(checkerMethod)));
}
boolean isBlocking = checkerMethod.hasDeclaredAnnotation(BLOCKING);
if (isBlocking && isReactive) {
throw new IllegalArgumentException("""
@PermissionChecker annotation instance placed on the '%s' returns 'Uni<Boolean>' and is
annotated with the @Blocking annotation; if you need to block, please return 'boolean'
""".formatted(toString(checkerMethod)));
}
var generatedPermissionClassName = getGeneratedPermissionName(checkerMethod, permissionCheckerIndex++);
var methodParamMappers = new MethodParameterMapper[checkerMethod.parametersCount()];
var generatedPermissionConstructor = getGeneratedPermissionConstructor(checkerMethod, methodParamMappers);
var checkerMetadata = new PermissionCheckerMetadata(checkerMethod, generatedPermissionClassName,
isReactive, generatedPermissionConstructor, methodParamMappers, isBlocking);
if (permissionCheckers.containsKey(permissionName)) {
throw new IllegalArgumentException("""
Detected two @PermissionChecker annotations with same value '%s', annotated methods are:
- %s
- %s
"""
.formatted(annotationInstance.value().asString(), toString(checkerMethod),View on GitHub (pinned to e1c734241f)
Solutions
- Remove the @Blocking annotation from the Uni<Boolean>-returning permission checker
- If the check must block (JDBC, remote call), change the return type from Uni<Boolean> to boolean
- Split the logic: a blocking boolean checker, or a reactive Uni<Boolean> checker without @Blocking
Example fix
// before
@PermissionChecker("book:read")
@Blocking
Uni<Boolean> canRead(Book book) { ... }
// after (blocking check)
@PermissionChecker("book:read")
boolean canRead(Book book) { ... }
// or reactive without blocking
@PermissionChecker("book:read")
Uni<Boolean> canRead(Book book) { ... } Defensive patterns
Strategy: validation
Validate before calling
Method m = MyBean.class.getMethod("canRead", Book.class);
if (m.getAnnotation(Blocking.class) != null && Uni.class.equals(m.getReturnType())) {
throw new IllegalStateException("Reactive permission checker must not be @Blocking");
} Prevention
- Adopt a convention: boolean return = blocking, Uni<Boolean> = never @Blocking
- Grep for 'Uni<Boolean>' methods annotated @Blocking in CI
- Keep permission checkers thin and non-blocking when reactive
When it happens
Trigger: Declaring a method like @PermissionChecker("x") @Blocking Uni<Boolean> check(...) — reactive return type combined with @Blocking — during the security deployment build step.
Common situations: Migrating a blocking permission checker to reactive (or vice versa) and leaving the old @Blocking annotation in place; cargo-culting @Blocking onto all security-related methods.
Related errors
- Blocking gRPC client call made from the event loop. If the c
- @Transactional cannot start a JTA transaction within a react
- @PermissionChecker annotation placed on the '%s' attribute '
- Detected two @PermissionChecker annotations with same value
- @PermissionChecker method '%s' declares checked exceptions w
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/1a1d24c139a260d2.
Report an issue: GitHub.