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
- Use a plain lvalue expression (a variable or property path like ${order.status}) as the assignment target
- Compute values in code/delegate and set the variable directly instead of assigning through a computed expression
- Re-check the flowable configuration field: replace 'expression' with 'delegateExpression' or a value expression as appropriate
- 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
- Use only plain variable/property paths as assignment targets
- Compute derived values in delegates, not in write-target expressions
- Parse-and-inspect the AST before setValue in generic tooling
- Review Flowable listener config fields that mix 'expression' and value semantics
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
- error.value.set.rvalue
- error.coerce.type
- ' + token.getImage() + '
- error.identifier.method.notamethod
- Lambda body is not an lvalue
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/6843b1e200651098.
Report an issue: GitHub.