flowable/flowable-engine · error · UnsupportedOperationException
Empty object, no variables can be set
Error message
Empty object, no variables can be set
What it means
EmptyVariableScope is an immutable no-op VariableScope placeholder (e.g. used for executions/tasks that no longer exist or as a null-scope stand-in in delegate expression evaluation). Every mutating method deliberately throws UnsupportedOperationException because an empty scope can never hold variables. Throwing here is the library's contract that mutation on this object is a programming error.
Solutions
- Verify the scope you hold is a real, still-active execution/task before mutating: check the scope type or re-fetch the execution via runtimeService.
- Move variable writes earlier in the lifecycle (before process instance end / completion) inside the actual delegate execution.
- Guard mutation with an instanceof/type check for EmptyVariableScope and skip or log instead of mutating.
- If you intentionally need a throwaway scope, use a custom VariableScope implementation instead of the empty sentinel.
Example fix
// before
scope.setVariable("status", "done");
// after
if (scope instanceof EmptyVariableScope) {
logger.warn("Skipping variable write: scope is empty/detached");
} else {
scope.setVariable("status", "done");
} Defensive patterns
Strategy: type-guard
Validate before calling
if (scope == null || scope instanceof EmptyVariableScope) {
throw new IllegalStateException("Scope is not writable");
} Type guard
boolean isWritableScope(VariableScope scope) {
return scope != null && !(scope instanceof EmptyVariableScope);
} Try / catch
try {
scope.setVariable("status", value);
} catch (UnsupportedOperationException e) {
if (e.getMessage() != null && e.getMessage().contains("Empty object")) {
logger.warn("Variable write skipped: scope is empty/detached");
} else {
throw e;
}
} Prevention
- Always mutate variables via the live VariableScope injected into delegates/listeners, never via cached references.
- Avoid writes after the process instance or task has completed.
- Add an EmptyVariableScope check at helper/utility boundaries that accept VariableScope.
- Fetch scopes fresh from runtimeService/taskService when in doubt.
When it happens
Trigger: Calling setVariable(String, Object) on an EmptyVariableScope instance, typically obtained when the delegate/execution resolves to the EMPTY sentinel scope instead of a real ExecutionEntity/TaskEntity.
Common situations: Delegate code (JavaDelegate/TaskListener) calling execution.setVariable(...) when the execution was replaced by EmptyVariableScope (e.g. after process instance end or in history/simulation contexts); passing EmptyVariableScope into helper code expecting a writable scope.
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
- Empty object, no variables can be created
- Empty object, no variables can be removed
- can't clear configuration beans
- can't search values in configuration beans
- Cannot change fixed value with
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/47469cdfcb5fdc83.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable-variable-service-api/src/main/java/org/flowable/variable/api/delegate/EmptyVariableScope.java:152
@Override
public VariableInstance getVariableInstance(String variableName, boolean fetchAllVariables) {
return null;
}
@Override
public VariableInstance getVariableInstanceLocal(String variableName) {
return null;
}
@Override
public VariableInstance getVariableInstanceLocal(String variableName, boolean fetchAllVariables) {
return null;
}
@Override
public void setVariable(String variableName, Object value) {
throw new UnsupportedOperationException("Empty object, no variables can be set");
}
@Override
public void setVariable(String variableName, Object value, boolean fetchAllVariables) {
throw new UnsupportedOperationException("Empty object, no variables can be set");
}
@Override
public Object setVariableLocal(String variableName, Object value) {
throw new UnsupportedOperationException("Empty object, no variables can be set");
}
@Override
public Object setVariableLocal(String variableName, Object value, boolean fetchAllVariables) {
throw new UnsupportedOperationException("Empty object, no variables can be set");
}
@OverrideView on GitHub (pinned to d6d39ce1c6)