elastic/elasticsearch · error · IllegalArgumentException

cannot convert function reference [%s::%s] to a non-function

Error message

cannot convert function reference [%s::%s] to a non-functional interface [%s]

What it means

FunctionRef.create throws this IllegalArgumentException at compile time when a Painless method-reference expression (Type::method) targets a class that is not a functional interface. Painless method references can only be converted to types that declare exactly one abstract method (a SAM type). The compiler calls lookupFunctionalInterfacePainlessMethod; if it returns null, the target has zero or multiple abstract methods and the reference cannot be bound.

Source

Thrown at modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionRef.java:78

        String methodName,
        int numberOfCaptures,
        Map<String, Object> constants,
        boolean needsScriptInstance
    ) {

        Objects.requireNonNull(painlessLookup);
        Objects.requireNonNull(targetClass);
        Objects.requireNonNull(typeName);
        Objects.requireNonNull(methodName);

        String targetClassName = PainlessLookupUtility.typeToCanonicalTypeName(targetClass);
        PainlessMethod interfaceMethod;

        try {
            interfaceMethod = painlessLookup.lookupFunctionalInterfacePainlessMethod(targetClass);

            if (interfaceMethod == null) {
                throw new IllegalArgumentException(
                    Strings.format(
                        "cannot convert function reference [%s::%s] to a non-functional interface [%s]",
                        typeName,
                        methodName,
                        targetClassName
                    )
                );
            }

            String interfaceMethodName = interfaceMethod.javaMethod().getName();
            MethodType interfaceMethodType = interfaceMethod.methodType().dropParameterTypes(0, 1);
            String delegateClassName;
            boolean isDelegateInterface;
            boolean isDelegateAugmented;
            int delegateInvokeType;
            String delegateMethodName;
            MethodType delegateMethodType;
            Object[] delegateInjections;

View on GitHub (pinned to db6a809a66)

Solutions

  1. Ensure the target type is a single-abstract-method interface (e.g. java.util.function.Function, Consumer, Predicate) that is whitelisted in Painless.
  2. Replace the method reference with an explicit lambda that implements the interface.
  3. Check the Painless allowlist for the interface; if it is missing, the method reference will not compile.

Example fix

// before
SomeClass ref = String::length;  // SomeClass is not a functional interface
// after
Function<String, Integer> ref = String::length;
Defensive patterns

Strategy: validation

Validate before calling

// Ensure the target type is a single-method interface before using a method reference:
// // java.util.function.Function, Consumer, Predicate, Supplier are valid functional interfaces

Prevention

When it happens

Trigger: Writing a Painless method reference where the target type is a marker interface, an abstract class with multiple methods, or a concrete class: 'Consumer c = List::size;' where List is not whitelisted as a functional interface, or assigning to a type Painless does not recognize as functional.

Common situations: Using method references with interfaces that Painless's whitelist does not mark as functional. Attempting to use a Java interface not allowed in scripts. Confusing a method reference target with a regular class type.

Related errors


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