quarkusio/quarkus · error · java.lang.RuntimeException
Static method '' cannot be annotated with the @PermissionChe
Error message
Static method '' cannot be annotated with the @PermissionChecker annotation
What it means
@PermissionChecker methods must be instance members of a CDI bean because the security runtime invokes them via the bean. Static methods cannot be proxied/called as bean members, so the deployment rejects them at build time.
Source
Thrown at extensions/security/deployment/src/main/java/io/quarkus/security/deployment/PermissionSecurityChecks.java:138
// and produce permission augmenter can and did in past run concurrently
this.permissionInstances = Collections.unmodifiableList(instances);
this.permissionNameToChecker = Collections.unmodifiableMap(getPermissionCheckers(index));
}
private static Map<String, PermissionCheckerMetadata> getPermissionCheckers(IndexView index) {
int permissionCheckerIndex = 0; // this ensures generated QuarkusPermission name is unique
var permissionCheckers = new HashMap<String, PermissionCheckerMetadata>();
for (var annotationInstance : index.getAnnotations(PERMISSION_CHECKER_NAME)) {
var checkerMethod = annotationInstance.target().asMethod();
if (Modifier.isPrivate(checkerMethod.flags())) {
// we generate QuarkusPermission in the same package as where the @PermissionChecker is detected
// so the checker method must be either public or package-private
throw new RuntimeException("Private method '" + toString(checkerMethod)
+ "' cannot be annotated with the @PermissionChecker annotation");
}
if (Modifier.isStatic(checkerMethod.flags())) {
// checkers must be CDI bean member methods for now, so the checker method must not be static
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("""View on GitHub (pinned to e1c734241f)
Solutions
- Remove the 'static' modifier so the method is an instance member of a CDI bean
- Ensure the enclosing class is a CDI bean (e.g. @ApplicationScoped) so the checker can be invoked
- If logic is shared statically, keep the static helper and have a non-static @PermissionChecker method call it
Example fix
// before
@PermissionChecker("can-view")
static boolean canView(Identity id) { return id.hasRole("viewer"); }
// after
@ApplicationScoped
class SecurityChecks {
@PermissionChecker("can-view")
boolean canView(Identity id) { return id.hasRole("viewer"); }
} Defensive patterns
Strategy: validation
Validate before calling
for (Method m : SecurityChecks.class.getDeclaredMethods()) {
if (m.isAnnotationPresent(PermissionChecker.class) && Modifier.isStatic(m.getModifiers())) {
throw new IllegalStateException("@PermissionChecker method must not be static: " + m.getName());
}
} Type guard
boolean isInstanceChecker(java.lang.reflect.Method m) {
return m.isAnnotationPresent(PermissionChecker.class) && !Modifier.isStatic(m.getModifiers());
} Try / catch
try {
appBootstrap();
} catch (RuntimeException e) {
if (e.getMessage() != null && e.getMessage().contains("Static method") && e.getMessage().contains("@PermissionChecker")) {
throw new IllegalStateException("Convert static @PermissionChecker to an instance method on a CDI bean", e);
}
throw e;
} Prevention
- Never mark @PermissionChecker methods static
- Enclose checkers in @ApplicationScoped CDI beans
- Delegate to static helpers instead of annotating them
- Include security bean conventions in code review checklists
When it happens
Trigger: Annotating a static method with @PermissionChecker and building the application; the build step getPermissionCheckers scans the Jandex index and throws.
Common situations: Developer writes a stateless utility-style checker as a static method (a common Java idiom) inside a bean or plain class, e.g. static boolean isOwner(...) annotated with @PermissionChecker, and the build fails.
Related errors
- Private method '' cannot be annotated with the @PermissionCh
- Unable to determine if the '${unsecuredMethod}' method shoul
- Interface '${declaringClass}' default method '${method}' has
- The '%s' CDI bean injection point was detected, but there is
- The @Blocking, @NonBlocking and @RunOnVirtualThread annotati
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/89e7edd5bf526e9a.
Report an issue: GitHub.