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

  1. Remove the @Blocking annotation from the Uni<Boolean>-returning permission checker
  2. If the check must block (JDBC, remote call), change the return type from Uni<Boolean> to boolean
  3. 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

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


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