flowable/flowable-engine · error · UnsupportedOperationException
UnsupportedOperationException
Error message
UnsupportedOperationException
What it means
VariableContainerWrapper is a simple adapter wrapping a variables map, and its default implementation does not support transient variables — setTransientVariable always throws UnsupportedOperationException. Transient variables (not persisted, valid only for the current transaction/thread) require a VariableContainer that explicitly implements this method.
Solutions
- Do not set transient variables on VariableContainerWrapper; use a real DelegateExecution/VariableScope obtained from the engine.
- Extend VariableContainerWrapper and override setTransientVariable if your use case needs in-memory transient storage.
- In tests, use a mock of DelegateExecution/VariableContainer that implements setTransientVariable (e.g. store in a local map).
Example fix
// before
VariableContainer c = new VariableContainerWrapper(map);
c.setTransientVariable("loopCounter2", 1); // throws
// after
VariableContainer c = new VariableContainerWrapper(map) {
@Override
public void setTransientVariable(String name, Object value) {
transientMap.put(name, value);
}
}; Defensive patterns
Strategy: type-guard
Validate before calling
if (container instanceof VariableContainerWrapper) {
throw new IllegalStateException("This code path sets transient variables; use a DelegateExecution-backed container");
} Type guard
boolean supportsTransient(VariableContainer c) {
try { c.setTransientVariable("__probe__", null); return true; }
catch (UnsupportedOperationException e) { return false; }
} Try / catch
try {
container.setTransientVariable(name, value);
} catch (UnsupportedOperationException e) {
container.setVariable(name, value); // fallback to a regular variable if transient semantics are not required
} Prevention
- Prefer a real DelegateExecution/VariableScope over VariableContainerWrapper when code sets transient variables.
- In test doubles, implement setTransientVariable with an in-memory map.
- Check Flowable upgrade notes: new internal paths may call setTransientVariable on custom containers.
When it happens
Trigger: Calling setTransientVariable(name, value) on a VariableContainerWrapper instance; passing a VariableContainerWrapper as the execution context to code (e.g. expression functions, delegates) that sets transient variables.
Common situations: Using VariableContainerWrapper to fake a DelegateExecution in unit tests or custom expression evaluation while the evaluated logic (e.g. a Flowable expression function or command) writes transient variables; upgrading Flowable where new internal code paths call setTransientVariable on containers that previously only needed setVariable.
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
- Cannot access id method/field for JPA Entity
- Cannot aggregate overview variable
- Cannot aggregate variable
- Cannot get variable value for jackson 2
- Cannot set global variable '
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/702fbf0faad97e14.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/el/VariableContainerWrapper.java:55
@Override
public boolean hasVariable(String variableName) {
return variables.containsKey(variableName);
}
@Override
public Object getVariable(String variableName) {
return variables.get(variableName);
}
@Override
public void setVariable(String variableName, Object variableValue) {
variables.put(variableName, variableValue);
}
@Override
public void setTransientVariable(String variableName, Object variableValue) {
throw new UnsupportedOperationException();
}
public String getInstanceId() {
return instanceId;
}
public void setInstanceId(String instanceId) {
this.instanceId = instanceId;
}
public String getScopeType() {
return scopeType;
}
public void setScopeType(String scopeType) {
this.scopeType = scopeType;
}
View on GitHub (pinned to d6d39ce1c6)