flowable/flowable-engine · error · FlowableIllegalArgumentException

Skip expression variable does not resolve to a boolean. " +

Error message

Skip expression variable does not resolve to a boolean. " + isSkipExpressionEnabled

What it means

SkipExpressionUtil.checkSkipExpressionVariable throws this FlowableIllegalArgumentException when the value stored under the DynamicBpmnConstants.ENABLE_SKIP_EXPRESSION variable is neither null nor a Boolean. The engine expects the skip-expression toggle to resolve strictly to a boolean.

Source

Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/helper/SkipExpressionUtil.java:74

        
        String skipExpressionEnabledVariable = "_ACTIVITI_SKIP_EXPRESSION_ENABLED";
        Object isSkipExpressionEnabled = execution.getVariable(skipExpressionEnabledVariable);

        if (isSkipExpressionEnabled instanceof Boolean) {
            return ((Boolean) isSkipExpressionEnabled).booleanValue();
        }

        skipExpressionEnabledVariable = "_FLOWABLE_SKIP_EXPRESSION_ENABLED";
        isSkipExpressionEnabled = execution.getVariable(skipExpressionEnabledVariable);

        if (isSkipExpressionEnabled == null) {
            return false;

        } else if (isSkipExpressionEnabled instanceof Boolean) {
            return ((Boolean) isSkipExpressionEnabled).booleanValue();

        } else {
            throw new FlowableIllegalArgumentException("Skip expression variable does not resolve to a boolean. " + isSkipExpressionEnabled);
        }
    }

    public static boolean shouldSkipFlowElement(String skipExpressionString, String activityId, DelegateExecution execution, CommandContext commandContext) {
        ExpressionManager expressionManager = CommandContextUtil.getProcessEngineConfiguration(commandContext).getExpressionManager();
        Expression skipExpression = expressionManager.createExpression(resolveActiveSkipExpression(skipExpressionString, activityId, 
                        execution.getProcessDefinitionId(), commandContext));
        
        Object value = skipExpression.getValue(execution);

        if (value instanceof Boolean) {
            return ((Boolean) value).booleanValue();

        } else {
            throw new FlowableIllegalArgumentException("Skip expression does not resolve to a boolean: " + skipExpression.getExpressionText());
        }
    }
    

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Set the variable as a Boolean: runtimeService.setVariable(executionId, "_FLOWABLE_SKIP_EXPRESSION_ENABLED", Boolean.TRUE).
  2. If the value comes from REST/JSON, parse it to Boolean before storing.
  3. If a String is unavoidable, evaluate it yourself and store the Boolean result, or remove the variable so the null branch (disabled) is used.
  4. Search dynamic BPMN override JSON for ENABLE_SKIP_EXPRESSION and correct its type to boolean.

Example fix

// before
vars.put("_FLOWABLE_SKIP_EXPRESSION_ENABLED", "true");

// after
vars.put("_FLOWABLE_SKIP_EXPRESSION_ENABLED", Boolean.parseBoolean("true"));
Defensive patterns

Strategy: validation

Validate before calling

Object v = runtimeService.getVariable(executionId, "_FLOWABLE_SKIP_EXPRESSION_ENABLED");
if (v != null && !(v instanceof Boolean)) {
    runtimeService.setVariable(executionId, "_FLOWABLE_SKIP_EXPRESSION_ENABLED",
            Boolean.parseBoolean(String.valueOf(v)));
}

Type guard

static boolean isSkipToggleValid(Object v) {
    return v == null || v instanceof Boolean;
}

Try / catch

try {
    SkipExpressionUtil.isSkipExpressionEnabled(...);
} catch (FlowableIllegalArgumentException e) {
    // treat as disabled and continue
}

Prevention

When it happens

Trigger: A process- or tenant-level variable (or dynamic BPMN override) named/served via ENABLE_SKIP_EXPRESSION holds a non-Boolean object (String "true", Integer 1, JSON value), and SkipExpressionUtil.isSkipExpressionEnabled evaluates it.

Common situations: Setting the variable via process variables with a String value instead of Boolean; dynamic BPMN overrides serializing values as JsonNode/raw types; scripts or REST calls writing "true"/1 instead of a real boolean; upgrading Flowable where the check became stricter.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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