flowable/flowable-engine · error · FlowableException

Variable ' ' is already present on task ' '.

Error message

Variable '${name}' is already present on task '${task.getId()}'.

What it means

REST create/update conflict in TaskVariableBaseResource.setVariable: a variable with the same name already exists on the task while a create operation was requested, so the existing variable must be updated instead.

Solutions

  1. Use PUT /runtime/tasks/{taskId}/variables/{variableName} to update the existing variable instead of POST
  2. Delete the variable first (DELETE endpoint) if you need to recreate it
  3. Check existing variables with GET /runtime/tasks/{taskId}/variables before creating
Defensive patterns

Strategy: validation

Validate before calling

const existing = await api.get(`/runtime/tasks/${taskId}/variables`); if (existing.data.some(v => v.name === name && v.variableScope === scope)) { /* update via PUT instead */ }

Try / catch

try { await api.post(`/runtime/tasks/${taskId}/variables`, body); } catch (e) { if (e.response && e.response.status === 409) { await api.put(`/runtime/tasks/${taskId}/variables/${name}`, body); } else { throw e; } }

Prevention

When it happens

Trigger: POST /runtime/tasks/{taskId}/variables with a body whose variable name already exists in the task's local (or chosen) scope. In setVariable(), isNew=true and hasVariableOnScope(task, name, scope) returns true.

Common situations: Re-running a script that creates variables; concurrent requests creating the same variable; confusing POST (create) with PUT (update) semantics; the variable exists locally while the client assumed global scope or vice versa.

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/7d6f0d27856f8334. Report an issue: GitHub.

Appendix: source

Thrown at modules/flowable-rest/src/main/java/org/flowable/rest/service/api/runtime/task/TaskVariableBaseResource.java:225

        // 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.name(), 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() + "' does not 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 (task.getExecutionId() != null) {

View on GitHub (pinned to d6d39ce1c6)