flowable/flowable-engine · error · FlowableIllegalArgumentException

Only allowed to update multiple variables in the same scope.

Error message

Only allowed to update multiple variables in the same scope.

What it means

All variables in one batch-creation request must target the same scope. createExecutionVariable records the scope of the first variable (defaulting to LOCAL) and throws FlowableIllegalArgumentException as soon as a later variable declares a different scope (GLOBAL vs LOCAL). Mixed-scope batches would make transactional semantics ambiguous, so they are rejected.

Solutions

  1. Split the batch into separate requests, one per scope (all GLOBAL in one call, all LOCAL in another)
  2. Set a uniform "scope" on every element of the array (or omit it so all default to LOCAL)
  3. Submit scope-specific variables through the dedicated endpoint (e.g. .../variables/{variableName} with scope in the body) instead of batching

Example fix

// before
[{"name":"a","scope":"global","value":1},{"name":"b","scope":"local","value":2}]
// after
[{"name":"a","scope":"global","value":1},{"name":"b","scope":"global","value":2}]  // second batch with scope "local" sent separately
Defensive patterns

Strategy: validation

Validate before calling

const scopes = new Set(variables.map(v => v.scope || 'local')); if (scopes.size > 1) throw new Error('batch contains mixed scopes: ' + [...scopes].join(','));

Type guard

function singleScope(vars) { const s = new Set(vars.map(v => v.scope || 'local')); return s.size <= 1; }

Try / catch

try { await api.post(url, vars); } catch (e) { if (e.message.includes('same scope')) { for (const scope of ['global','local']) { const batch = vars.filter(v => (v.scope||'local') === scope); if (batch.length) await api.post(url, batch); } } }

Prevention

When it happens

Trigger: POSTing a variables array like [{"name":"a","scope":"global"},{"name":"b","scope":"local"}] to a variables collection endpoint.

Common situations: Merging variables from two different contexts into one request; copying an example that used GLOBAL and appending LOCAL variables; generic UIs that let users pick scope per variable but batch-submit all at once.

Related errors


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

Appendix: source

Thrown at modules/flowable-rest/src/main/java/org/flowable/rest/service/api/runtime/process/BaseVariableCollectionResource.java:126

            RestVariableScope sharedScope = null;
            RestVariableScope varScope = null;
            Map<String, Object> variablesToSet = new HashMap<>();

            for (RestVariable var : inputVariables) {
                // Validate if scopes match
                varScope = var.getVariableScope();
                if (var.getName() == null) {
                    throw new FlowableIllegalArgumentException("Variable name is required");
                }

                if (varScope == null) {
                    varScope = RestVariableScope.LOCAL;
                }
                if (sharedScope == null) {
                    sharedScope = varScope;
                }
                if (varScope != sharedScope) {
                    throw new FlowableIllegalArgumentException("Only allowed to update multiple variables in the same scope.");
                }

                if (!override && hasVariableOnScope(execution, var.getName(), varScope)) {
                    throw new FlowableConflictException("Variable '" + var.getName() + "' is already present on execution '" + execution.getId() + "'.");
                }

                Object actualVariableValue = restResponseFactory.getVariableValue(var);
                variablesToSet.put(var.getName(), actualVariableValue);
            }

            if (!variablesToSet.isEmpty()) {
                if (restApiInterceptor != null) {
                    restApiInterceptor.createExecutionVariables(execution, variablesToSet, sharedScope);
                }

                Map<String, Object> setVariables = null;
                if (sharedScope == RestVariableScope.LOCAL) {
                    

View on GitHub (pinned to d6d39ce1c6)