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
- Make the expression evaluate to a boolean: use a proper boolean comparison like '${nrOfCompletedInstances >= 3}' or reference a Boolean-typed variable.
- Fix the backing variable so it is stored as a Boolean, not a String ('true'), e.g. execution.setVariable("done", Boolean.TRUE).
- Change the delegated bean/service method invoked by the expression to return a primitive/Boolean result.
- 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
- Store completion flags as Boolean, never 'true'/'false' strings.
- Write completion conditions as explicit boolean comparisons (e.g. ${nrOfCompletedInstances >= nrOfInstances}).
- Ensure beans invoked in conditions return boolean/Boolean.
- Unit-test completion condition expressions with the expression manager before deployment.
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
- ${collectionExpressionText}' didn't resolve to a Collection
- Variable '${obj}':${collectionVariable} is not a Collection
- Couldn't resolve collection expression${collectionExpression
- Could not resolve loopCardinality expression '${loopCardinal
- Skip expression does not resolve to a boolean: " + skipExpre
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/b38637076e87237d.
Report an issue: GitHub.