{"record":{"id":"8b98e437a0882e46","repo":"flowable/flowable-engine","slug":"completioncondition-expressiontext-does-not-e","errorCode":null,"errorMessage":"completionCondition '${expressionText}' does not evaluate to a boolean value","messagePattern":"completionCondition '(.+?)' does not evaluate to a boolean value","errorType":"validation","errorClass":"ActivitiIllegalArgumentException","httpStatus":null,"severity":"error","filePath":"modules/flowable5-engine/src/main/java/org/activiti/engine/impl/bpmn/behavior/MultiInstanceActivityBehavior.java","lineNumber":218,"sourceCode":"\n    protected int resolveLoopCardinality(ActivityExecution execution) {\n        // Using Number since expr can evaluate to eg. Long (which is also the default for Juel)\n        Object value = loopCardinalityExpression.getValue(execution);\n        if (value instanceof Number) {\n            return ((Number) value).intValue();\n        } else if (value instanceof String) {\n            return Integer.valueOf((String) value);\n        } else {\n            throw new ActivitiIllegalArgumentException(\"Could not resolve loopCardinality expression '\"\n                    + loopCardinalityExpression.getExpressionText() + \"': not a number nor number String\");\n        }\n    }\n\n    protected boolean completionConditionSatisfied(ActivityExecution execution) {\n        if (completionConditionExpression != null) {\n            Object value = completionConditionExpression.getValue(execution);\n            if (!(value instanceof Boolean)) {\n                throw new ActivitiIllegalArgumentException(\"completionCondition '\"\n                        + completionConditionExpression.getExpressionText()\n                        + \"' does not evaluate to a boolean value\");\n            }\n            Boolean booleanValue = (Boolean) value;\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    protected void setLoopVariable(ActivityExecution execution, String variableName, Object value) {\n        execution.setVariableLocal(variableName, value);\n    }\n\n    protected Integer getLoopVariable(ActivityExecution execution, String variableName) {\n        Object value = execution.getVariableLocal(variableName);","sourceCodeStart":200,"sourceCodeEnd":236,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable5-engine/src/main/java/org/activiti/engine/impl/bpmn/behavior/MultiInstanceActivityBehavior.java#L200-L236","documentation":"Thrown by MultiInstanceActivityBehavior.completionConditionSatisfied when a multi-instance activity defines a completion condition whose expression evaluates to something other than a java.lang.Boolean. The engine only branches on a strict Boolean, so any other result type makes the condition indeterminable and it throws ActivitiIllegalArgumentException.","triggerScenarios":"activiti:completionCondition=\"${x}\" where x is a String \"true\", an Integer 0/1, or any non-Boolean object; UEL does not coerce truthiness, so ${nrCompleted >= nrInstances} is fine but a variable holding \"true\" as String is not.","commonSituations":"Returning a String from a delegate instead of Boolean; using a script/expression that yields null when variables are missing; porting models from engines/languages with looser truthiness rules (JS-style) into Flowable 5.","solutions":["Change the completion condition so it evaluates to a boolean, e.g. activiti:completionCondition=\"${nrOfCompletedInstances/nrOfInstances >= 0.6}\"","Wrap non-boolean logic with Boolean.parseBoolean() in a listener/delegate and expose a real Boolean variable","Remove the completionCondition attribute if the intent was to complete only when all instances finish"],"exampleFix":"// before\n<multiInstanceLoopCharacteristics ... activiti:completionCondition=\"${completeFlag}\"/> <!-- completeFlag is String \"true\" -->\n// after\nexecution.setVariable(\"completeFlag\", Boolean.parseBoolean(rawFlag)); // real Boolean","handlingStrategy":"validation","validationCode":"Object v = execution.getVariable(\"completeFlag\");\nif (!(v instanceof Boolean)) {\n    throw new IllegalStateException(\"completion condition variable must be a Boolean\");\n}","typeGuard":"public static boolean isBoolean(Object v) {\n    return v instanceof Boolean;\n}","tryCatchPattern":"try {\n    runtimeService.signal(executionId);\n} catch (org.activiti.engine.ActivitiIllegalArgumentException e) {\n    if (e.getMessage().contains(\"does not evaluate to a boolean value\")) {\n        // fix the completionCondition expression or variable type\n    }\n}","preventionTips":["Write completion conditions as boolean expressions (${nrOfCompletedInstances == nrOfInstances}) rather than variable lookups","If a variable is used, ensure a delegate stores a real java.lang.Boolean","Avoid Boolean.parseBoolean strings crossing into process variables meant for conditions","Review BPMN models during migration: engines with truthiness coercion will surface this in Flowable 5"],"tags":["bpmn","multi-instance","expression-evaluation","boolean"],"backgroundTag":"invalid-argument-value","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-18T16:17:23.245Z"}