flowable/flowable-engine · error · UnsupportedOperationException
Empty object, no variables can be set
Error message
Empty object, no variables can be set
What it means
EmptyVariableContainer is a read-only, immutable variable scope that always returns null for getVariable; its setVariable/setTransientVariable deliberately throw UnsupportedOperationException("Empty object, no variables can be set") because the container represents an empty scope and cannot store any variables.
Solutions
- Write variables to a real, mutable scope (DelegateExecution, DelegateTask, VariableScope from the execution context) instead of the empty container
- Check whether the container is an EmptyVariableContainer (or check writability) before writing
- If you only need reads, keep in mind getVariable always returns null — obtain actual variables from the execution instead
Example fix
// before
VariableScope scope = resolveScope();
scope.setVariable("approved", true); // throws if EmptyVariableContainer
// after
VariableScope scope = resolveScope();
if (scope instanceof EmptyVariableContainer) {
throw new IllegalStateException("Cannot write variables to empty scope");
}
scope.setVariable("approved", true); Defensive patterns
Strategy: type-guard
Validate before calling
// ensure the scope is writable before setting variables
if (scope instanceof EmptyVariableContainer) {
throw new IllegalStateException("Scope is read-only (empty); cannot set variables");
} Type guard
static boolean isWritableScope(VariableScope scope) {
return !(scope instanceof EmptyVariableContainer);
} Try / catch
try {
scope.setVariable("approved", true);
} catch (UnsupportedOperationException e) {
LOGGER.error("Attempted to write variables to an empty variable container");
throw new IllegalStateException("Use a real DelegateExecution/Task scope for writes", e);
} Prevention
- Obtain variable scopes from DelegateExecution/DelegateTask, not from empty-container placeholders
- Check writability before bulk variable writes in generic code
- Remember EmptyVariableContainer.getVariable always returns null — reads need the real scope too
- In tests, use a simple in-memory VariableScope implementation instead of EmptyVariableContainer when writes are needed
When it happens
Trigger: Calling setVariable(name, value) or setTransientVariable(name, value) on an EmptyVariableContainer instance — e.g. code obtained a variable scope that is the empty container and then attempts to write.
Common situations: Delegate/listener code writing variables to a scope resolved in a context with no variables (such as certain delegate-execution placeholders), or tests using EmptyVariableContainer and then attempting writes.
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
- Not allowed to set variables in a bpmn error variable…
- Couldn't deserialize object in variable
- couldn't find a variable type that is able to serialize
- Couldn't serialize value '' in variable ''
- Couldn't serialize value
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/4756698fc639a458.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable-engine-common-api/src/main/java/org/flowable/common/engine/api/variable/EmptyVariableContainer.java:37
* @author Arthur Hupka-Merle
*/
class EmptyVariableContainer implements VariableContainer {
static final EmptyVariableContainer INSTANCE = new EmptyVariableContainer();
@Override
public boolean hasVariable(String variableName) {
return false;
}
@Override
public Object getVariable(String variableName) {
return null;
}
@Override
public void setVariable(String variableName, Object variableValue) {
throw new UnsupportedOperationException("Empty object, no variables can be set");
}
@Override
public void setTransientVariable(String variableName, Object variableValue) {
throw new UnsupportedOperationException("Empty object, no variables can be set");
}
@Override
public String getTenantId() {
return null;
}
@Override
public Set<String> getVariableNames() {
return Collections.emptySet();
}
}
View on GitHub (pinned to d6d39ce1c6)