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

  1. Use PUT to update an existing variable instead of POST
  2. Check existence first via GET /tasks/{id}/variables/{name}
  3. Make client scripts idempotent by using PUT or upsert logic
  4. 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

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


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)