{"record":{"id":"c7c16545a0b8933a","repo":"elastic/elasticsearch","slug":"entitlement-class-has-non-static-method-annot","errorCode":null,"errorMessage":"entitlement class [{}] has non-static method annotated with ExternalEntitlement","messagePattern":"entitlement class \\[(.+?)\\] has non-static method annotated with ExternalEntitlement","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyParser.java","lineNumber":243,"sourceCode":"        for (var ctor : entitlementClass.getConstructors()) {\n            var metadata = ctor.getAnnotation(ExternalEntitlement.class);\n            if (metadata != null) {\n                if (entitlementMetadata != null) {\n                    throw new IllegalStateException(\n                        \"entitlement class [\"\n                            + entitlementClass.getName()\n                            + \"] has more than one constructor annotated with ExternalEntitlement\"\n                    );\n                }\n                entitlementConstructor = ctor;\n                entitlementMetadata = metadata;\n            }\n        }\n        for (var method : entitlementClass.getMethods()) {\n            var metadata = method.getAnnotation(ExternalEntitlement.class);\n            if (metadata != null) {\n                if (Modifier.isStatic(method.getModifiers()) == false) {\n                    throw new IllegalStateException(\n                        \"entitlement class [\" + entitlementClass.getName() + \"] has non-static method annotated with ExternalEntitlement\"\n                    );\n                }\n                if (entitlementMetadata != null) {\n                    throw new IllegalStateException(\n                        \"entitlement class [\"\n                            + entitlementClass.getName()\n                            + \"] has more than one constructor and/or method annotated with ExternalEntitlement\"\n                    );\n                }\n                entitlementMethod = method;\n                entitlementMetadata = metadata;\n            }\n        }\n\n        if (entitlementMetadata == null) {\n            throw newPolicyParserException(scopeName, \"unknown entitlement type [\" + entitlementType + \"]\");\n        }","sourceCodeStart":225,"sourceCodeEnd":261,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyParser.java#L225-L261","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)."],"exampleFix":"// before\npublic class MyEntitlement implements Entitlement {\n    @ExternalEntitlement(parameterNames = {\"paths\"})\n    public List<Object> build(List<Object> paths) { ... }\n}\n\n// after\npublic class MyEntitlement implements Entitlement {\n    @ExternalEntitlement(parameterNames = {\"paths\"})\n    public static MyEntitlement build(List<Object> paths) { ... }\n}","handlingStrategy":"validation","validationCode":"public static void assertAnnotatedMethodsAreStatic(Class<?> entitlementClass) {\n    for (Method m : entitlementClass.getDeclaredMethods()) {\n        if (m.isAnnotationPresent(ExternalEntitlement.class)\n            && !Modifier.isStatic(m.getModifiers())) {\n            throw new AssertionError(m + \" is @ExternalEntitlement but not static\");\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["entitlements","policy","annotation","reflection"],"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-12T06:17:24.410Z"}