elastic/elasticsearch · error · IllegalStateException
entitlement class [{}] has non-static method annotated with
Error message
entitlement class [{}] has non-static method annotated with ExternalEntitlement What it means
Thrown when PolicyParser's reflection scan finds an @ExternalEntitlement annotation on a non-static method. The parser invokes the annotated method without an instance, so it must be static; the annotation's @Target allows METHOD but the runtime additionally enforces static-ness.
Source
Thrown at libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyParser.java:243
for (var ctor : entitlementClass.getConstructors()) {
var metadata = ctor.getAnnotation(ExternalEntitlement.class);
if (metadata != null) {
if (entitlementMetadata != null) {
throw new IllegalStateException(
"entitlement class ["
+ entitlementClass.getName()
+ "] has more than one constructor annotated with ExternalEntitlement"
);
}
entitlementConstructor = ctor;
entitlementMetadata = metadata;
}
}
for (var method : entitlementClass.getMethods()) {
var metadata = method.getAnnotation(ExternalEntitlement.class);
if (metadata != null) {
if (Modifier.isStatic(method.getModifiers()) == false) {
throw new IllegalStateException(
"entitlement class [" + entitlementClass.getName() + "] has non-static method annotated with ExternalEntitlement"
);
}
if (entitlementMetadata != null) {
throw new IllegalStateException(
"entitlement class ["
+ entitlementClass.getName()
+ "] has more than one constructor and/or method annotated with ExternalEntitlement"
);
}
entitlementMethod = method;
entitlementMetadata = metadata;
}
}
if (entitlementMetadata == null) {
throw newPolicyParserException(scopeName, "unknown entitlement type [" + entitlementType + "]");
}View on GitHub (pinned to db6a809a66)
Solutions
- Make the annotated method static (add the `static` keyword).
- If the method genuinely needs instance state, redesign so the entitlement is built from static inputs (the parser only passes parsed policy values, never an instance).
Example fix
// before
public class MyEntitlement implements Entitlement {
@ExternalEntitlement(parameterNames = {"paths"})
public List<Object> build(List<Object> paths) { ... }
}
// after
public class MyEntitlement implements Entitlement {
@ExternalEntitlement(parameterNames = {"paths"})
public static MyEntitlement build(List<Object> paths) { ... }
} Defensive patterns
Strategy: validation
Validate before calling
public static void assertAnnotatedMethodsAreStatic(Class<?> entitlementClass) {
for (Method m : entitlementClass.getDeclaredMethods()) {
if (m.isAnnotationPresent(ExternalEntitlement.class)
&& !Modifier.isStatic(m.getModifiers())) {
throw new AssertionError(m + " is @ExternalEntitlement but not static");
}
}
} Prevention
- Always declare @ExternalEntitlement methods as `static`.
- Add a reflection-based test that fails the build when an annotated method is non-static.
- Review diffs that change a static factory into an instance method.
When it happens
Trigger: A developer adds @ExternalEntitlement to an instance method of an Entitlement class. The error fires during the first policy parse that references this entitlement type.
Common situations: Converting a static factory to an instance method while leaving the annotation in place; new entitlement author forgets the static requirement; refactoring that turns a holder class into a non-inner class with instance state.
Related errors
- entitlement class [{}] has more than one constructor annotat
- entitlement class [{}] has more than one constructor and/or
- Error occurred when inspecting class: {}; SerializedLambda r
- Error occurred when inspecting class: {}
- Proxy fallback only supports instance method references; no
AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12).
Data as JSON: /api/errors/c7c16545a0b8933a.
Report an issue: GitHub.