{"record":{"id":"b38637076e87237d","repo":"flowable/flowable-engine","slug":"completioncondition-activecompletioncondition","errorCode":null,"errorMessage":"completionCondition '${activeCompletionCondition}' does not evaluate to a boolean value","messagePattern":"completionCondition '(.+?)' does not evaluate to a boolean value","errorType":"validation","errorClass":"FlowableIllegalArgumentException","httpStatus":null,"severity":"error","filePath":"modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/behavior/MultiInstanceActivityBehavior.java","lineNumber":393,"sourceCode":"        if (completionCondition != null) {\n            \n            ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration();\n            ExpressionManager expressionManager = processEngineConfiguration.getExpressionManager();\n            \n            String activeCompletionCondition = null;\n\n            if (CommandContextUtil.getProcessEngineConfiguration().isEnableProcessDefinitionInfoCache()) {\n                ObjectNode taskElementProperties = BpmnOverrideContext.getBpmnOverrideElementProperties(activity.getId(), execution.getProcessDefinitionId());\n                activeCompletionCondition = getActiveValue(completionCondition, DynamicBpmnConstants.MULTI_INSTANCE_COMPLETION_CONDITION, taskElementProperties);\n\n            } else {\n                activeCompletionCondition = completionCondition;\n            }\n            \n            Object value = expressionManager.createExpression(activeCompletionCondition).getValue(execution);\n            \n            if (!(value instanceof Boolean booleanValue)) {\n                throw new FlowableIllegalArgumentException(\"completionCondition '\" + activeCompletionCondition + \"' does not evaluate to a boolean value\");\n            }\n\n            if (LOGGER.isDebugEnabled()) {\n                LOGGER.debug(\"Completion condition of multi-instance satisfied: {}\", booleanValue);\n            }\n            return booleanValue;\n        }\n        return false;\n    }\n    \n    public Integer getLoopVariable(DelegateExecution execution, String variableName) {\n        VariableInstance variable = getLoopVariableInstance(execution, variableName);\n        Object value = variable != null ? variable.getValue() : 0;\n        return (Integer) (value != null ? value : 0);\n    }\n\n    public VariableInstance getLoopVariableInstance(DelegateExecution execution, String variableName) {\n        VariableInstance variable = execution.getVariableInstanceLocal(variableName);","sourceCodeStart":375,"sourceCodeEnd":411,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/behavior/MultiInstanceActivityBehavior.java#L375-L411","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: variable stored as String\nexecution.setVariable(\"allApproved\", \"true\");\n// after: store as Boolean so the completion condition evaluates to a boolean\nexecution.setVariable(\"allApproved\", Boolean.TRUE);","handlingStrategy":"validation","validationCode":"Object v = execution.getVariable(\"allApproved\");\nif (!(v instanceof Boolean)) {\n    throw new IllegalStateException(\"completionCondition variable must be Boolean, got: \"\n        + (v == null ? \"null\" : v.getClass().getName()));\n}","typeGuard":"boolean isBooleanValue(Object v) { return v instanceof Boolean; }","tryCatchPattern":"try {\n    return multiInstanceBehavior.completionConditionSatisfied(execution);\n} catch (FlowableIllegalArgumentException ex) {\n    logger.error(\"completionCondition must evaluate to boolean: {}\", ex.getMessage());\n    throw ex;\n}","preventionTips":["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."],"tags":["bpmn","multi-instance","expression","type-mismatch"],"backgroundTag":"type-mismatch","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}