{"record":{"id":"ba82f97b50c6bd26","repo":"elastic/elasticsearch","slug":"class-is-not-a-functional-interface","errorCode":null,"errorMessage":"Class [{}] is not a functional interface","messagePattern":"Class \\[(.+?)\\] is not a functional interface","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java","lineNumber":495,"sourceCode":"     */\n    static MethodHandle lookupReference(\n        PainlessLookup painlessLookup,\n        FunctionTable functions,\n        Map<String, Object> constants,\n        MethodHandles.Lookup methodHandlesLookup,\n        String interfaceClass,\n        Class<?> receiverClass,\n        String name,\n        boolean chargesAllocation\n    ) throws Throwable {\n\n        Class<?> interfaceType = painlessLookup.canonicalTypeNameToType(interfaceClass);\n        if (interfaceType == null) {\n            throw new IllegalArgumentException(\"type [\" + interfaceClass + \"] not found\");\n        }\n        PainlessMethod interfaceMethod = painlessLookup.lookupFunctionalInterfacePainlessMethod(interfaceType);\n        if (interfaceMethod == null) {\n            throw new IllegalArgumentException(\"Class [\" + interfaceClass + \"] is not a functional interface\");\n        }\n        int arity = interfaceMethod.typeParameters().size();\n        PainlessMethod implMethod = painlessLookup.lookupRuntimePainlessMethod(receiverClass, name, arity);\n        if (implMethod == null) {\n            throw new IllegalArgumentException(\n                \"dynamic method [\" + typeToCanonicalTypeName(receiverClass) + \", \" + name + \"/\" + arity + \"] not found\"\n            );\n        }\n\n        // Charging def-receiver ref: the script was over-captured (receiver type unknown, so no pre-filter). Charge only if\n        // the resolved target has an estimator; either way lookupReferenceInternal appends the script and drops it.\n        return lookupReferenceInternal(\n            painlessLookup,\n            functions,\n            constants,\n            methodHandlesLookup,\n            interfaceType,\n            PainlessLookupUtility.typeToCanonicalTypeName(implMethod.targetClass()),","sourceCodeStart":477,"sourceCodeEnd":513,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java#L477-L513","documentation":"Def.lookupReference, after resolving interfaceType successfully, calls painlessLookup.lookupFunctionalInterfacePainlessMethod(interfaceType). If that returns null, the resolved type is not actually a functional interface (no single abstract method), and the code throws 'Class [...] is not a functional interface'. This catches cases where the type name is whitelisted but the type itself cannot serve as a lambda target.","triggerScenarios":"A Painless lambda/method-reference targets a whitelisted type that is not a functional interface — e.g. an interface with zero or multiple abstract methods, a marker interface, or a class (non-interface) type that happened to be registered.","commonSituations":"Misidentifying a type with multiple methods as functional. Using a whitelisted non-functional type (e.g. an abstract class) as a lambda target. Allowlist changes that converted a functional interface into a richer one.","solutions":["Target a true functional interface (exactly one abstract method) — typically from java.util.function.","Check the resolved type's abstract method count; if more than one, choose the specific SAM type you need.","Register a proper functional interface in the allowlist if a custom one is intended."],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":null,"typeGuard":"// Verify a type is a functional interface (exactly one abstract method) before using as lambda target.\n// In tooling: reflection MethodCount abstract == 1; or restrict to a known SAM set.","tryCatchPattern":"// On 'Class [...] is not a functional interface', instruct the author to choose a SAM type from java.util.function.","preventionTips":["Target only single-abstract-method interfaces for lambdas.","Validate the chosen type has exactly one abstract method in script authoring tooling."],"tags":["painless","script","dynamic-dispatch","def","lambda","type-resolution"],"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-12T06:17:24.410Z"}