flowable/flowable-engine · error · ELException

error.value.set.rvalue

Error message

error.value.set.rvalue

What it means

ELException with message error.value.set.rvalue: setValue() was called on an expression AST node that is not an lvalue (the node was parsed as a right-hand value only). The library refuses to assign through an expression that has no assignable target, such as a literal, arithmetic result, or method call. This is a programming/config error in which the expression was never intended to be a write target.

Source

Thrown at modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/de/odysseus/el/tree/impl/ast/AstProperty.java:144

		if (base == null) {
			throw new PropertyNotFoundException(LocalMessages.get("error.property.base.null", prefix));
		}
		Object property = getProperty(bindings, context);
		if (property == null && strict) {
			throw new PropertyNotFoundException(LocalMessages.get("error.property.property.notfound", "null", base));
		}
		context.setPropertyResolved(false);
		boolean result = context.getELResolver().isReadOnly(context, base, property);
		if (!context.isPropertyResolved()) {
			throw new PropertyNotFoundException(LocalMessages.get("error.property.property.notfound", property, base));
		}
		return result;
	}

	@Override
	public void setValue(Bindings bindings, ELContext context, Object value) throws ELException {
		if (!lvalue) {
			throw new ELException(LocalMessages.get("error.value.set.rvalue", getStructuralId(bindings)));
		}
		Object base = prefix.eval(bindings, context);
		if (base == null) {
			throw new PropertyNotFoundException(LocalMessages.get("error.property.base.null", prefix));
		}
		Object property = getProperty(bindings, context);
		if (property == null && strict) {
			throw new PropertyNotFoundException(LocalMessages.get("error.property.property.notfound", "null", base));
		}
		context.setPropertyResolved(false);
		Class<?> type = context.getELResolver().getType(context, base, property);
		if (context.isPropertyResolved()) {
			if (type != null && (value != null || type.isPrimitive())) {
				value = bindings.convert(value, type);
			}
			context.setPropertyResolved(false);
		}
		context.getELResolver().setValue(context, base, property, value);

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Use a plain lvalue expression (a variable or property path like ${order.status}) as the assignment target
  2. Compute values in code/delegate and set the variable directly instead of assigning through a computed expression
  3. Re-check the flowable configuration field: replace 'expression' with 'delegateExpression' or a value expression as appropriate
  4. Parse the expression and inspect the AST if you need to validate lvalue-ness before calling setValue

Example fix

// before
expression: "${order.total + surcharge}"  // used as set target -> rvalue
// after
expression: "${order.totalWithSurcharge}" // plain property lvalue; compute elsewhere
Defensive patterns

Strategy: validation

Validate before calling

// reject rvalue expressions before calling setValue
if (!expressionText.matches("\\$\\{[A-Za-z_$][\\w$.]*\\}")) {
    throw new IllegalArgumentException("Not an lvalue expression: " + expressionText);
}

Type guard

boolean isLvalue(AstNode n) { return n instanceof AstProperty || n instanceof AstIdentifier; }

Try / catch

try {
    node.setValue(bindings, context, value);
} catch (ELException e) {
    log.error("Cannot assign through rvalue expression", e);
    throw new IllegalArgumentException("Expression must be an assignable path", e);
}

Prevention

When it happens

Trigger: Calling setValue() on an AstProperty created with lvalue=false (e.g. parsed from a non-assignment expression) or on rvalue node types; using an expression like ${1+1} or ${bean.compute()} as a set target in a task listener / variable mapping.

Common situations: Flowable task listener 'expression' fields configured with computed expressions where an assignment was expected; misuse of the EL API attempting to write into method-call results; copy-paste of a read expression into a write slot of a variable scope configuration.

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/6843b1e200651098. Report an issue: GitHub.