flowable/flowable-engine · error · ELException

error.method.literal.void

error.method.literal.void

Error message

error.method.literal.void

What it means

TreeMethodExpression validates its expression at construction. A literal-text expression (e.g. 'someString' with no #{} or ${}) used as a method expression must have a non-void return type; throwing a literal that returns void is rejected with 'error.method.literal.void'.

Source

Thrown at modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/de/odysseus/el/TreeMethodExpression.java:88

	 * @param returnType the expected return type (may be <code>null</code>)
	 * @param paramTypes the expected parameter types (must not be <code>null</code> for lvalues)
	 */
	public TreeMethodExpression(TreeStore store, FunctionMapper functions, VariableMapper variables, TypeConverter converter, String expr, Class<?> returnType, Class<?>[] paramTypes) {
		super();

		Tree tree = store.get(expr);

		this.builder = store.getBuilder();
		this.bindings = tree.bind(functions, variables, converter);
		this.expr = expr;
		this.type = returnType;
		this.types = paramTypes;
		this.node = tree.getRoot();
		this.deferred = tree.isDeferred();

		if (node.isLiteralText()) {
			if (returnType == void.class || returnType == Void.class) {
				throw new ELException(LocalMessages.get("error.method.literal.void", expr));
			}
		} else if (!node.isMethodInvocation()) {
			if (!node.isLeftValue()) {
				throw new ELException(LocalMessages.get("error.method.invalid", expr));
			}
			if (paramTypes == null) {
				throw new NullPointerException(LocalMessages.get("error.method.notypes")); // EL specification requires NPE
			}
		}
	}

	private String getStructuralId() {
		if (structure == null) {
			structure = node.getStructuralId(bindings);
		}
		return structure;
	}
	

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Change the expected return type to a non-void type (e.g. Object.class) if the literal is intentional.
  2. Replace the literal expression with an actual method expression like '#{bean.action}' for void-returning actions.
  3. Remove the expression binding if a constant literal was not intended to be invoked.

Example fix

// before
new TreeMethodExpression(store, ctx, fns, vars, "doIt", null, void.class)
// after
new TreeMethodExpression(store, ctx, fns, vars, "#{bean.doIt}", null, void.class)
Defensive patterns

Strategy: validation

Validate before calling

if (returnType == void.class || returnType == Void.class) {
    if (!expr.contains("#{") && !expr.contains("${")) {
        throw new IllegalArgumentException("literal method expression cannot have void return type: " + expr);
    }
}

Type guard

boolean isLiteralVoidMethod(String expr, Class<?> returnType) {
    boolean literal = !(expr.startsWith("#{") || expr.startsWith("${"));
    return literal && (returnType == void.class || returnType == Void.class);
}

Try / catch

try {
    TreeMethodExpression m = new TreeMethodExpression(store, ctx, fns, vars, expr, paramTypes, returnType);
} catch (ELException e) {
    // literal with void return: use a method expression or non-void returnType
}

Prevention

When it happens

Trigger: new TreeMethodExpression(..., expr, paramTypes, returnType) where expr is literal text (node.isLiteralText()) and returnType is void.class or Void.class.

Common situations: JSF-style action listeners or UI bindings configured with a literal string instead of a method reference; refactoring that changed a method expression into a literal while keeping a void return type.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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