flowable/flowable-engine · error · ELException

error.coerce.nonAbstract

error.coerce.nonAbstract

Error message

error.coerce.nonAbstract

What it means

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.

Source

Thrown at modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/de/odysseus/el/misc/TypeConverterImpl.java:363

				try {
					editor.setAsText(value);
				} catch (IllegalArgumentException e) {
					throw new ELException(LocalMessages.get("error.coerce.value", value, value.getClass(), type), e);
				}
			}
			return editor.getValue();
		}
	}

	protected <T> T coerceToFunctionalInterface(LambdaExpression lambdaExpression, Class<T> type) {
		Supplier<T> proxy = () -> {
			// Create a dynamic proxy for the functional interface
			@SuppressWarnings("unchecked")
			T result = (T) Proxy.newProxyInstance(type.getClassLoader(), new Class[] { type },
					(Object obj, Method method, Object[] args) -> {
						// Functional interfaces have a single, abstract method
						if (!Modifier.isAbstract(method.getModifiers())) {
							throw new ELException(LocalMessages.get("error.coerce.nonAbstract", type, method));
						}
						return lambdaExpression.invoke(args);
					});
			return result;
		};
		return proxy.get();
	}

    protected Object coerceToPrimitive(Object value, Class<?> type) {
        if (type == long.class) {
            return coerceToLong(value);
        }
        if (type == double.class) {
            return coerceToDouble(value);
        }
        if (type == boolean.class) {
            return coerceToBoolean(value);
        }

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Invoke only the single abstract method of the target interface through the proxy; refactor callers that rely on default methods.
  2. Change the target type to a plain functional interface without default methods.
  3. Upgrade JUEL/Odysseus EL to a version whose proxy handler handles non-abstract (default/Object) methods properly.
  4. Catch ELException around the lambda invocation and handle the unsupported method dispatch.

Example fix

// before
MyFn f = (MyFn) lambda; f.defaultHelper(); // default method -> ELException
// after
MyFn f = (MyFn) lambda; f.apply(x); // call only the SAM
Defensive patterns

Strategy: try-catch

Validate before calling

// ensure the target is a true SAM interface without default methods used at runtime
for (Method m : targetType.getMethods()) {
    if (!Modifier.isAbstract(m.getModifiers()) && m.getDeclaringClass() != Object.class && !m.isDefault()) {
        // suspicious target for lambda coercion
    }
}

Type guard

boolean isSamInterface(Class<?> t) {
    if (t == null || !t.isInterface()) return false;
    Method[] abs = t.getMethods();
    return Arrays.stream(t.getMethods())
        .filter(m -> Modifier.isAbstract(m.getModifiers()))
        .count() == 1;
}

Try / catch

try {
    result = samProxy.invoke(args);
} catch (ELException e) {
    log.warn("Non-abstract method dispatched on lambda proxy", e);
    result = handleDefaultMethod(args);
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11). Data as JSON: /api/errors/63e3f17cd96aeb63. Report an issue: GitHub.