{"record":{"id":"63e3f17cd96aeb63","repo":"flowable/flowable-engine","slug":"error-coerce-nonabstract","errorCode":"error.coerce.nonAbstract","errorMessage":"error.coerce.nonAbstract","messagePattern":"error\\.coerce\\.nonAbstract","errorType":"exception","errorClass":"ELException","httpStatus":null,"severity":"error","filePath":"modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/de/odysseus/el/misc/TypeConverterImpl.java","lineNumber":363,"sourceCode":"\t\t\t\ttry {\n\t\t\t\t\teditor.setAsText(value);\n\t\t\t\t} catch (IllegalArgumentException e) {\n\t\t\t\t\tthrow new ELException(LocalMessages.get(\"error.coerce.value\", value, value.getClass(), type), e);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn editor.getValue();\n\t\t}\n\t}\n\n\tprotected <T> T coerceToFunctionalInterface(LambdaExpression lambdaExpression, Class<T> type) {\n\t\tSupplier<T> proxy = () -> {\n\t\t\t// Create a dynamic proxy for the functional interface\n\t\t\t@SuppressWarnings(\"unchecked\")\n\t\t\tT result = (T) Proxy.newProxyInstance(type.getClassLoader(), new Class[] { type },\n\t\t\t\t\t(Object obj, Method method, Object[] args) -> {\n\t\t\t\t\t\t// Functional interfaces have a single, abstract method\n\t\t\t\t\t\tif (!Modifier.isAbstract(method.getModifiers())) {\n\t\t\t\t\t\t\tthrow new ELException(LocalMessages.get(\"error.coerce.nonAbstract\", type, method));\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn lambdaExpression.invoke(args);\n\t\t\t\t\t});\n\t\t\treturn result;\n\t\t};\n\t\treturn proxy.get();\n\t}\n\n    protected Object coerceToPrimitive(Object value, Class<?> type) {\n        if (type == long.class) {\n            return coerceToLong(value);\n        }\n        if (type == double.class) {\n            return coerceToDouble(value);\n        }\n        if (type == boolean.class) {\n            return coerceToBoolean(value);\n        }","sourceCodeStart":345,"sourceCodeEnd":381,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/de/odysseus/el/misc/TypeConverterImpl.java#L345-L381","documentation":"When coercing a lambda to a functional-interface type, JUEL builds a dynamic Proxy that forwards every call to the lambda. The proxy's handler assumes each invoked Method is the interface's single abstract method; if a non-abstract method (e.g. a default method or toString) is dispatched through the handler and is not abstract, it throws this ELException.","triggerScenarios":"A lambda expression is coerced (coerceToFunctionalInterface) to a target functional interface, and at invocation time the proxy dispatches a Method that fails Modifier.isAbstract, e.g. a default method on the interface being called.","commonSituations":"Coercing lambdas to interfaces that include default methods (Java 8+ interfaces like Comparator with reversed()); calling Object methods or default methods through the proxy; custom functional interfaces with defensive default methods.","solutions":["Invoke only the single abstract method of the target interface through the proxy; refactor callers that rely on default methods.","Change the target type to a plain functional interface without default methods.","Upgrade JUEL/Odysseus EL to a version whose proxy handler handles non-abstract (default/Object) methods properly.","Catch ELException around the lambda invocation and handle the unsupported method dispatch."],"exampleFix":"// before\nMyFn f = (MyFn) lambda; f.defaultHelper(); // default method -> ELException\n// after\nMyFn f = (MyFn) lambda; f.apply(x); // call only the SAM","handlingStrategy":"try-catch","validationCode":"// ensure the target is a true SAM interface without default methods used at runtime\nfor (Method m : targetType.getMethods()) {\n    if (!Modifier.isAbstract(m.getModifiers()) && m.getDeclaringClass() != Object.class && !m.isDefault()) {\n        // suspicious target for lambda coercion\n    }\n}","typeGuard":"boolean isSamInterface(Class<?> t) {\n    if (t == null || !t.isInterface()) return false;\n    Method[] abs = t.getMethods();\n    return Arrays.stream(t.getMethods())\n        .filter(m -> Modifier.isAbstract(m.getModifiers()))\n        .count() == 1;\n}","tryCatchPattern":"try {\n    result = samProxy.invoke(args);\n} catch (ELException e) {\n    log.warn(\"Non-abstract method dispatched on lambda proxy\", e);\n    result = handleDefaultMethod(args);\n}","preventionTips":["Only coerce lambdas to plain SAM interfaces without default methods.","Do not call default or Object methods through EL lambda proxies.","Pin/upgrade the EL library version known to handle default methods.","Add tests invoking every method your code calls on coerced lambdas."],"tags":["el","lambda","proxy","functional-interface"],"backgroundTag":"unsupported-operation","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}