flowable/flowable-engine · error · FlowableIllegalArgumentException

Cannot set global variable '

Error message

Cannot set global variable '

What it means

FlowableIllegalArgumentException thrown in setVariable when attempting to set a variable with GLOBAL scope on a standalone CMMN task that is not linked to a case (no scopeId). Standalone tasks cannot hold global/process-level variables; only local task variables are allowed. If the task IS part of a case, the variable is set on the case instance via runtimeService.

Source

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

        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) {
                // Explicitly set on execution, setting non-local variable on
                // task will override local-variable if exists
                runtimeService.setVariable(task.getScopeId(), name, value);
            } else {
                // Standalone task, no global variables possible
                throw new FlowableIllegalArgumentException("Cannot set global variable '" + name + "' on task '" + task.getId() + "', task is not part of process.");
            }
        }
    }
}

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Omit the scope field or set scope=local for standalone tasks
  2. Only use scope=global when the task belongs to a case instance
  3. Query the task first to confirm scopeId is set before sending global-scoped variables

Example fix

// before
{"name":"v","value":1,"scope":"global"}   // standalone task
// after
{"name":"v","value":1,"scope":"local"}  (or omit scope)
Defensive patterns

Strategy: validation

Validate before calling

const task = await getTask(taskId);
if (scope === 'global' && !task.scopeId) throw new Error(`task ${taskId} is standalone; global scope is not allowed`);

Type guard

const canSetGlobal = (task) => task != null && typeof task.scopeId === 'string' && task.scopeId.length > 0;

Try / catch

try { await setVariable({...v, scope:'global'}) } catch (e) { if (/Cannot set global variable/.test(e.message)) return setVariable({...v, scope:'local'}); throw e; }

Prevention

When it happens

Trigger: POST/PUT with scope=global on a standalone task (task.getScopeId() == null) — Flowable falls into the else branch and rejects the operation.

Common situations: Clients blanket-applying scope:'global' to all variable calls; migrating REST scripts from process-task usage to standalone task usage; misunderstanding that standalone tasks have no case instance to attach global variables to.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11). Data as JSON: /api/errors/7179d79a024c0c17. Report an issue: GitHub.