flowable/flowable-engine · error · FlowableException
Variable '
Error message
Variable '
What it means
FlowableException thrown in setVariable when creating a new variable (POST, isNew=true) for a name that already exists on the task in the requested scope. Variables must be created once; existing variables must be updated via PUT. The full message includes the variable name and task id.
Solutions
- Use PUT to update an existing variable instead of POST
- Check existence first via GET /tasks/{id}/variables/{name}
- Make client scripts idempotent by using PUT or upsert logic
- Delete the variable first if recreation is intended
Example fix
// before: re-POST existing variable
POST /cmmn-runtime/tasks/123/variables [{"name":"status","value":"x"}]
// after
PUT /cmmn-runtime/tasks/123/variables/status {"value":"x"} Defensive patterns
Strategy: validation
Validate before calling
const existing = await getTaskVariable(taskId, name).catch(() => null);
if (existing) throw new Error(`variable '${name}' already exists on task ${taskId}; use PUT`); Try / catch
try { await postVariable(v) } catch (e) { if (/is already present on task/.test(e.message)) return putVariable(v); throw e; } Prevention
- Treat POST as create-only, PUT as update-only
- Make client scripts idempotent (upsert or existence check)
- Avoid re-running failed batch scripts without checking state
When it happens
Trigger: POST /cmmn-runtime/tasks/{taskId}/variables with a variable whose name already exists on that task (local or global scope), instead of PUT /cmmn-runtime/tasks/{taskId}/variables/{name}.
Common situations: Re-running idempotency-broken client scripts; double submission of the same creation request; confusion between POST (create) and PUT (update) semantics.
Understand the failure class
Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.
Related errors
- Variable '" + name + "' is already present on execution '"…
- Cannot set global variable '
- Converter can only convert big decimal values
- Converter can only convert big integer values
- Converter can only convert booleans
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/8ce6305401e638d3.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable-cmmn-rest/src/main/java/org/flowable/cmmn/rest/service/api/runtime/task/TaskVariableBaseResource.java:226
// Figure out scope, revert to local is omitted
RestVariableScope scope = restVariable.getVariableScope();
if (scope == null) {
scope = RestVariableScope.LOCAL;
}
Object actualVariableValue = restResponseFactory.getVariableValue(restVariable);
setVariable(task, restVariable.getName(), actualVariableValue, scope, isNew);
return getVariableFromRequestWithoutAccessCheck(task, restVariable.getName(), scope, false);
}
protected void setVariable(Task task, String name, Object value, RestVariableScope scope, boolean isNew) {
// Create can only be done on new variables. Existing variables should
// be updated using PUT
boolean hasVariable = hasVariableOnScope(task, name, scope);
if (isNew && hasVariable) {
throw new FlowableException("Variable '" + name + "' is already present on task '" + task.getId() + "'.");
}
if (!isNew && !hasVariable) {
throw new FlowableObjectNotFoundException("Task '" + task.getId() + "' doesn't have a variable with name: '" + name + "'.", null);
}
if (restApiInterceptor != null) {
if (isNew) {
restApiInterceptor.createTaskVariables(task, Collections.singletonMap(name, value), scope);
} else {
restApiInterceptor.updateTaskVariables(task, Collections.singletonMap(name, value), scope);
}
}
if (scope == RestVariableScope.LOCAL) {
taskService.setVariableLocal(task.getId(), name, value);
} else {
if (ScopeTypes.CMMN.equals(task.getScopeType()) && task.getScopeId() != null) {View on GitHub (pinned to d6d39ce1c6)