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

  1. Add 'String[] PARAMETERS = {"name1", "name2", ...};' as a public static final field on the interface.
  2. Ensure the field name is exactly PARAMETERS (case-sensitive) and is public.
  3. 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

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


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