flowable/flowable-engine · error · ActivitiException
variable ' ' already exists. Use setVariableLocal if you…
Error message
variable '${variableName}' already exists. Use setVariableLocal if you want to overwrite the value What it means
VariableScopeImpl.createVariableLocal is only invoked when a brand-new local variable is created on the scope; it asserts the variable name does not already exist and otherwise throws this ActivitiException. It is an internal invariant guard for setVariableLocal's create path — callers should use setVariableLocal which overwrites, or check existence first. Hitting it means a race or stale scope allowed a duplicate creation attempt.
Solutions
- Use setVariableLocal (or setVariable) which overwrites existing values instead of the create-only path
- Guard creation with a check: hasVariableLocal(name) before creating, or just always assign
- Serialize updates to the same execution (avoid parallel writes to one variable) or rely on optimistic locking and retry the command
- Rename variables per branch (e.g. prefix with branch id) to avoid cross-scope collisions
Example fix
// before
task.setVariableLocal("approval", value); // may hit duplicate create under concurrency
// after
if (!task.hasVariableLocal("approval")) {
task.setVariableLocal("approval", value);
} else {
task.setVariableLocal("approval", value); // overwrite path
} Defensive patterns
Strategy: try-catch
Validate before calling
boolean exists = scope.hasVariableLocal(variableName);
Try / catch
try {
createVariableLocal(name, value, sourceExecution);
} catch (ActivitiException e) {
if (e.getMessage().contains("already exists")) {
scope.setVariableLocal(name, value); // overwrite instead
}
} Prevention
- Prefer setVariableLocal (upsert semantics) over create-only paths
- Avoid concurrent writes to the same variable on one execution; retry on optimistic lock
- Use distinct variable names per branch/scope
- Check hasVariableLocal before creating
When it happens
Trigger: Creating a local variable on an execution/task whose variableInstances map already contains that name — e.g. concurrent setVariableLocal calls from two threads/transactions, or an internal create path invoked when initialization revealed the variable already present.
Common situations: Parallel branches or multiple service tasks setting the same local variable name simultaneously; retrying a failed transaction whose first attempt partially created the variable; custom code mixing setVariable and setVariableLocal on the same name; two engine nodes racing on the same execution without optimistic-lock handling.
Related errors
- Cannot set global variable '
- Invalid variable scope
- joining scope executions is not allowed
- joining scope executions is not allowed
- Only allowed to update multiple variables in the same scope.
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/8f7b2690dd194581.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable5-engine/src/main/java/org/activiti/engine/impl/persistence/entity/VariableScopeImpl.java:822
}
return null;
}
}
public void createVariableLocal(String variableName, Object value) {
createVariableLocal(variableName, value, getSourceActivityExecution());
}
/**
* only called when a new variable is created on this variable scope. This method is also responsible for propagating the creation of this variable to the history.
*/
protected void createVariableLocal(String variableName, Object value, ExecutionEntity sourceActivityExecution) {
ensureVariableInstancesInitialized();
if (variableInstances.containsKey(variableName)) {
throw new ActivitiException("variable '" + variableName + "' already exists. Use setVariableLocal if you want to overwrite the value");
}
createVariableInstance(variableName, value, sourceActivityExecution);
}
@Override
public void removeVariable(String variableName) {
removeVariable(variableName, getSourceActivityExecution());
}
protected void removeVariable(String variableName, ExecutionEntity sourceActivityExecution) {
ensureVariableInstancesInitialized();
if (variableInstances.containsKey(variableName)) {
removeVariableLocal(variableName);
return;
}
VariableScopeImpl parentVariableScope = getParentVariableScope();
if (parentVariableScope != null) {View on GitHub (pinned to d6d39ce1c6)