elastic/elasticsearch · error · IllegalStateException
entitlement class [{}] has more than one constructor annotat
Error message
entitlement class [{}] has more than one constructor annotated with ExternalEntitlement What it means
Thrown by PolicyParser while scanning an Entitlement class's constructors via reflection. The parser allows at most one constructor per class to carry @ExternalEntitlement; finding a second annotated constructor means the class declares two competing entry points and the parser cannot decide which to invoke when materialising the entitlement from a policy file.
Source
Thrown at libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyParser.java:229
}
}
protected Entitlement parseEntitlement(String scopeName, String entitlementType) throws IOException {
XContentLocation startLocation = policyParser.getTokenLocation();
Class<?> entitlementClass = externalEntitlements.get(entitlementType);
if (entitlementClass == null) {
throw newPolicyParserException(scopeName, "unknown entitlement type [" + entitlementType + "]");
}
Constructor<?> entitlementConstructor = null;
Method entitlementMethod = null;
ExternalEntitlement entitlementMetadata = null;
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) {View on GitHub (pinned to db6a809a66)
Solutions
- Inspect the named class and remove @ExternalEntitlement from all but one constructor.
- If you genuinely need multiple construction shapes, route them through a single annotated static factory method instead and remove annotations from all constructors.
- Rebuild and re-run the policy parse to confirm only one annotated constructor remains.
Example fix
// before
public final class MyEntitlement implements Entitlement {
@ExternalEntitlement(parameterNames = {"a"})
public MyEntitlement(String a) { ... }
@ExternalEntitlement(parameterNames = {"b"})
public MyEntitlement(int b) { ... }
}
// after
public final class MyEntitlement implements Entitlement {
public MyEntitlement(String a) { ... }
@ExternalEntitlement(parameterNames = {"a"})
public static MyEntitlement build(String a) { return new MyEntitlement(a); }
} Defensive patterns
Strategy: validation
Validate before calling
// At test time, assert each @ExternalEntitlement class has exactly one annotated member.
public static void assertSingleAnnotatedMember(Class<?> entitlementClass) {
int ctors = Arrays.stream(entitlementClass.getConstructors())
.filter(c -> c.isAnnotationPresent(ExternalEntitlement.class))
.toList().size();
int methods = Arrays.stream(entitlementClass.getDeclaredMethods())
.filter(m -> m.isAnnotationPresent(ExternalEntitlement.class))
.toList().size();
if (ctors + methods != 1) {
throw new AssertionError(entitlementClass.getName() + " must have exactly one @ExternalEntitlement member, found "
+ ctors + " ctor(s), " + methods + " method(s)");
}
} Prevention
- Run a unit test that scans every Entitlement class for exactly one @ExternalEntitlement member.
- Prefer static factory methods over annotated constructors to make the entry point obvious.
- Add an architectural test (ArchUnit or similar) forbidding multiple annotations per class.
When it happens
Trigger: An Entitlement implementation class has two (or more) constructors, and both are annotated with @ExternalEntitlement. The error fires the first time PolicyParser.parseEntitlement resolves that class for any scope that references its entitlement type.
Common situations: Authoring a new Entitlement subclass and copy-pasting the @ExternalEntitlement annotation onto an overloaded constructor; refactoring a constructor into a builder-style multi-ctor setup without removing the annotation from the old one; merging two entitlement classes during a refactor.
Related errors
- entitlement class [{}] has non-static method annotated with
- 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/584e08bdf6649d86.
Report an issue: GitHub.