elastic/elasticsearch · error · IllegalArgumentException
Painless needs a constant [String[] PARAMETERS] on all inter
Error message
Painless needs a constant [String[] PARAMETERS] on all interfaces it implements with the names of the method arguments but [{}] doesn't have one. What it means
readArgumentNamesConstant reflectively looks up a public field named PARAMETERS on the context interface. If no such field exists (NoSuchFieldException), this IllegalArgumentException is thrown with the interface name and the original exception chained. Every Painless-implementable interface must declare 'public static final String[] PARAMETERS'.
Source
Thrown at modules/lang-painless/src/main/java/org/elasticsearch/painless/ScriptClassInfo.java:280
Class<?> componentType = type;
while (componentType.isArray()) {
componentType = componentType.getComponentType();
}
if (componentType != def.class && painlessLookup.lookupPainlessClass(componentType) == null) {
throw new IllegalArgumentException(unknownErrorMessageSource.apply(componentType));
}
return type;
}
private static String[] readArgumentNamesConstant(Class<?> iface) {
Field argumentNamesField;
try {
argumentNamesField = iface.getField("PARAMETERS");
} catch (NoSuchFieldException e) {
throw new IllegalArgumentException(
"Painless needs a constant [String[] PARAMETERS] on all interfaces it implements with the "
+ "names of the method arguments but ["
+ iface.getName()
+ "] doesn't have one.",
e
);
}
if (false == argumentNamesField.getType().equals(String[].class)) {
throw new IllegalArgumentException(
"Painless needs a constant [String[] PARAMETERS] on all interfaces it implements with the "
+ "names of the method arguments but ["
+ iface.getName()
+ "] doesn't have one."
);
}
try {
return (String[]) argumentNamesField.get(null);
} catch (IllegalArgumentException | IllegalAccessException e) {View on GitHub (pinned to db6a809a66)
Solutions
- Add 'String[] PARAMETERS = {"name1", "name2", ...};' as a public static final field on the interface.
- Ensure the field name is exactly PARAMETERS (case-sensitive) and is public.
- Match the array length to execute's parameter count (see error 1344).
Example fix
// before
public interface MyScript {
double execute(Map<String,Object> params); // no PARAMETERS -> 1345
}
// after
public interface MyScript {
String[] PARAMETERS = {"params"};
double execute(Map<String,Object> params);
} Defensive patterns
Strategy: validation
Validate before calling
void assertHasParametersField(Class<?> iface) {
try { iface.getField("PARAMETERS"); }
catch (NoSuchFieldException e) {
throw new IllegalStateException("Add public static final String[] PARAMETERS to " + iface.getName());
}
} Try / catch
try {
new ScriptClassInfo(lookup, MyScript.class);
} catch (IllegalArgumentException e) {
if (e.getMessage().contains("doesn't have one") && e.getCause() instanceof NoSuchFieldException) {
fail("Declare PARAMETERS field on the interface");
}
throw e;
} Prevention
- Always declare 'public static final String[] PARAMETERS' on any Painless context interface.
- Use the existing built-in contexts (FilterScript, ScoreScript) as templates.
- Add a CI check that scans context interfaces for the PARAMETERS field.
When it happens
Trigger: Registering a custom script-context interface that omits the PARAMETERS field entirely, names it differently (e.g. ARG_NAMES), or declares it non-public so getField cannot see it.
Common situations: Hand-writing a context interface and forgetting the PARAMETERS constant; copy-pasting from a non-Painless interface; field declared as private or with a typo in the name.
Related errors
- Painless can only implement interfaces that have a single me
- no execute method found
- convertFromDef must take a single Object as an argument, not
- [{}#ARGUMENTS] has length [2] but [{}#execute] takes [1] arg
- Error trying to read [{}#ARGUMENTS]
AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12).
Data as JSON: /api/errors/c9e789928047293c.
Report an issue: GitHub.