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

  1. Make the annotated method static (add the `static` keyword).
  2. 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

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


AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12). Data as JSON: /api/errors/c7c16545a0b8933a. Report an issue: GitHub.