flowable/flowable-engine · error · FlowableIllegalArgumentException

completionCondition '${activeCompletionCondition}' does not

Error message

completionCondition '${activeCompletionCondition}' does not evaluate to a boolean value

What it means

Thrown by MultiInstanceActivityBehavior.completionConditionSatisfied when the multi-instance activity's completionCondition expression evaluates to something that is not a java.lang.Boolean. The condition string is valid enough to be created as an expression, but its value must be a boolean to decide whether the remaining instances can be terminated early.

Source

Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/behavior/MultiInstanceActivityBehavior.java:393

        if (completionCondition != null) {
            
            ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration();
            ExpressionManager expressionManager = processEngineConfiguration.getExpressionManager();
            
            String activeCompletionCondition = null;

            if (CommandContextUtil.getProcessEngineConfiguration().isEnableProcessDefinitionInfoCache()) {
                ObjectNode taskElementProperties = BpmnOverrideContext.getBpmnOverrideElementProperties(activity.getId(), execution.getProcessDefinitionId());
                activeCompletionCondition = getActiveValue(completionCondition, DynamicBpmnConstants.MULTI_INSTANCE_COMPLETION_CONDITION, taskElementProperties);

            } else {
                activeCompletionCondition = completionCondition;
            }
            
            Object value = expressionManager.createExpression(activeCompletionCondition).getValue(execution);
            
            if (!(value instanceof Boolean booleanValue)) {
                throw new FlowableIllegalArgumentException("completionCondition '" + activeCompletionCondition + "' does not evaluate to a boolean value");
            }

            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Completion condition of multi-instance satisfied: {}", booleanValue);
            }
            return booleanValue;
        }
        return false;
    }
    
    public Integer getLoopVariable(DelegateExecution execution, String variableName) {
        VariableInstance variable = getLoopVariableInstance(execution, variableName);
        Object value = variable != null ? variable.getValue() : 0;
        return (Integer) (value != null ? value : 0);
    }

    public VariableInstance getLoopVariableInstance(DelegateExecution execution, String variableName) {
        VariableInstance variable = execution.getVariableInstanceLocal(variableName);

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Make the expression evaluate to a boolean: use a proper boolean comparison like '${nrOfCompletedInstances >= 3}' or reference a Boolean-typed variable.
  2. Fix the backing variable so it is stored as a Boolean, not a String ('true'), e.g. execution.setVariable("done", Boolean.TRUE).
  3. Change the delegated bean/service method invoked by the expression to return a primitive/Boolean result.
  4. Guard the expression with an explicit boolean conversion, e.g. '${condition != null && condition == true}' only if condition is a Boolean; otherwise convert at the source.

Example fix

// before: variable stored as String
execution.setVariable("allApproved", "true");
// after: store as Boolean so the completion condition evaluates to a boolean
execution.setVariable("allApproved", Boolean.TRUE);
Defensive patterns

Strategy: validation

Validate before calling

Object v = execution.getVariable("allApproved");
if (!(v instanceof Boolean)) {
    throw new IllegalStateException("completionCondition variable must be Boolean, got: "
        + (v == null ? "null" : v.getClass().getName()));
}

Type guard

boolean isBooleanValue(Object v) { return v instanceof Boolean; }

Try / catch

try {
    return multiInstanceBehavior.completionConditionSatisfied(execution);
} catch (FlowableIllegalArgumentException ex) {
    logger.error("completionCondition must evaluate to boolean: {}", ex.getMessage());
    throw ex;
}

Prevention

When it happens

Trigger: A <multiInstanceLoopCharacteristics><completionCondition> whose expression returns a String, Integer, null or other non-Boolean object, e.g. '${condition}' pointing at a variable holding 'true' (String) instead of a boolean.

Common situations: Storing completion flags as strings ('true'/'false') in process variables; expressions calling a service method that returns Integer or Optional instead of boolean; typos producing null (e.g. ${myCondtion}); JUEL evaluating a numeric comparison wrapped so the result is boxed as non-Boolean.

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