flowable/flowable-engine · error · FlowableException
Cannot set value of '', it's readonly!
Error message
Cannot set value of '', it's readonly!
What it means
ReadOnlyMapELResolver is an EL resolver that exposes a read-only map of variables to Flowable expressions. When an expression attempts to assign a value to a property backed by the wrapped map, the resolver deliberately rejects the write because the exposed map is intended to be immutable within expression evaluation. This guards engine-managed variables from being mutated mid-expression.
Solutions
- Replace the assignment expression with a write through the engine API (e.g. taskService.setVariable / execution.setVariable, or delegateExecution.setVariable in a delegate) instead of mutating variables inside EL.
- If writes are required, register a writable resolver/VariableContainer (e.g. VariableContainerWrapper with setVariable support) instead of ReadOnlyMapELResolver.
- Set the variable before the expression evaluation, not inside the expression itself.
- If the write is intentional inside a script, use a script task with proper variable mapping (in/out mappings) rather than EL assignment.
Example fix
// before
expression="${orderStatus = 'APPROVED'}"
// after
<flowable:field name="expression" expression="${orderStatus}" />
// plus in a delegate: execution.setVariable("orderStatus", "APPROVED"); Defensive patterns
Strategy: validation
Validate before calling
if (expression != null && expression.matches(".*[^=!<>+\-*/]=[^=].*")) {
throw new IllegalArgumentException("Expression must not assign to read-only variables: " + expression);
} Type guard
boolean isReadOnlyVariable(VariableContainer c, String name) {
return c instanceof ReadOnlyMapELResolver; // writes via setVariable are rejected for read-only containers
} Try / catch
try {
managementService.executeCommand(new ExpressionSetCommand(expression));
} catch (FlowableException e) {
if (e.getMessage() != null && e.getMessage().contains("readonly")) {
throw new IllegalArgumentException("Assignments are not allowed on read-only variables");
}
throw e;
} Prevention
- Never use assignment (=) inside Flowable EL expressions; write variables via the engine API.
- Expose only copies/maps intended for reading to expression resolvers.
- Use delegateExecution.setVariable in delegates instead of expression-side effects.
- Review process definitions for '=' inside ${...} expressions during validation.
When it happens
Trigger: Evaluating a Flowable expression that contains an assignment (e.g. ${myVar = 'x'} in a script/expression) where myVar is a key in the read-only map passed to the resolver; calling setValue on the resolver directly with a base of null and a property contained in wrappedMap.
Common situations: Using an assignment-style expression in a BPMN service task or listener expression where a read-only variable map (e.g. task/execution variables exposed read-only) is mutated; copying an expression written for a writable variable scope into a read-only context; custom code injecting ReadOnlyMapELResolver and then expecting variable writes to work.
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.value.set.rvalue
- error.method.invalid
- Error while evaluating expression
- Error while executing input entry
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/c26a55938beb34da.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/el/ReadOnlyMapELResolver.java:56
if (base == null) {
if (wrappedMap.containsKey(property)) {
context.setPropertyResolved(true);
return wrappedMap.get(property);
}
}
return null;
}
@Override
public boolean isReadOnly(ELContext context, Object base, Object property) {
return true;
}
@Override
public void setValue(ELContext context, Object base, Object property, Object value) {
if (base == null) {
if (wrappedMap.containsKey(property)) {
throw new FlowableException("Cannot set value of '" + property + "', it's readonly!");
}
}
}
@Override
public Class<?> getCommonPropertyType(ELContext context, Object arg) {
return Object.class;
}
@Override
public Class<?> getType(ELContext context, Object arg1, Object arg2) {
return Object.class;
}
}
View on GitHub (pinned to d6d39ce1c6)