{"record":{"id":"391a335594665d5c","repo":"flowable/flowable-engine","slug":"expression-condition-condition-did-not-evaluate","errorCode":null,"errorMessage":"Expression condition ${condition} did not evaluate to a boolean value with ${variableContainer}","messagePattern":"Expression condition (.+?) did not evaluate to a boolean value with (.+?)","errorType":"exception","errorClass":"FlowableException","httpStatus":null,"severity":"error","filePath":"modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/util/ExpressionUtil.java","lineNumber":48,"sourceCode":"import org.flowable.common.engine.api.FlowableIllegalArgumentException;\nimport org.flowable.common.engine.api.delegate.Expression;\nimport org.flowable.common.engine.api.variable.VariableContainer;\nimport org.flowable.common.engine.impl.interceptor.CommandContext;\n\n/**\n * @author Joram Barrez\n * @author Micha Kiener\n */\npublic class ExpressionUtil {\n\n    public static boolean evaluateBooleanExpression(CommandContext commandContext, VariableContainer variableContainer, String condition) {\n        Object evaluationResult = evaluateExpression(commandContext, variableContainer, condition);\n        if (evaluationResult instanceof Boolean) {\n            return (boolean) evaluationResult;\n        } else if (evaluationResult instanceof String) {\n            return \"true\".equals(((String) evaluationResult).toLowerCase());\n        } else {\n            throw new FlowableException(\"Expression condition \" + condition + \" did not evaluate to a boolean value with \" + variableContainer);\n        }\n    }\n\n    public static Object evaluateExpression(CommandContext commandContext, VariableContainer variableContainer, String expression) {\n        Expression exp = CommandContextUtil.getExpressionManager(commandContext).createExpression(expression);\n        return exp.getValue(variableContainer);\n    }\n\n    public static boolean isRequiredPlanItemInstance(CommandContext commandContext, PlanItemInstanceEntity planItemInstanceEntity) {\n        PlanItemControl planItemControl = planItemInstanceEntity.getPlanItem().getItemControl();\n        if (planItemControl != null && planItemControl.getRequiredRule() != null) {\n\n            boolean isRequired = true; // Having a required rule means required by default, unless the condition says otherwise\n            String requiredCondition = planItemControl.getRequiredRule().getCondition();\n            if (StringUtils.isNotEmpty(requiredCondition)) {\n                isRequired = evaluateBooleanExpression(commandContext, planItemInstanceEntity, requiredCondition);\n            }\n            return isRequired;","sourceCodeStart":30,"sourceCodeEnd":66,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/util/ExpressionUtil.java#L30-L66","documentation":"ExpressionUtil.evaluateBooleanExpression evaluates a condition expression and accepts only Boolean results or the strings 'true'/'false' (case-insensitive). Any other result type triggers this FlowableException, because CMMN rules (condition/activation/auto-complete) demand a definitive boolean. It protects against silently treating non-boolean results as false.","triggerScenarios":"A plan item condition, ifPart, autoComplete condition or repetition condition expression returns e.g. a number, a collection, a date, or null — e.g. ${myVar} where myVar is an Integer, or a script fragment returning an object — during evaluation by isRequiredPlanItemInstance or evaluateAutoComplete.","commonSituations":"Expressions copied from scripts returning objects; conditions referencing unset variables evaluating to null; users writing conditions like ${amount} instead of ${amount > 0}; language packs where expression engines return Strings other than true/false.","solutions":["Rewrite the condition to yield a boolean comparison, e.g. ${amount > 0} instead of ${amount}","If the expression returns a String, ensure it is exactly 'true' or 'false' (case-insensitive)","Check for missing/null variables in the variableContainer that make the expression return null; initialize defaults","Unit-test the expression against representative variable values before deploying the case model"],"exampleFix":"// before\ncondition=\"${orderStatus}\"\n// after\ncondition=\"${orderStatus == 'APPROVED'}\"","handlingStrategy":"type-guard","validationCode":"Object result = evaluateExpression(cmdCtx, container, condition);\nif (!(result instanceof Boolean) && !(result instanceof String s && (\"true\".equalsIgnoreCase(s) || \"false\".equalsIgnoreCase(s)))) {\n    throw new IllegalStateException(\"Condition must yield boolean, got: \" + result);\n}","typeGuard":"boolean isBooleanExpressionResult(Object v) { return v instanceof Boolean || (v instanceof String s && (\"true\".equalsIgnoreCase(s) || \"false\".equalsIgnoreCase(s))); }","tryCatchPattern":"try { return ExpressionUtil.evaluateBooleanExpression(cmdCtx, container, condition); } catch (FlowableException e) { if (e.getMessage().contains(\"did not evaluate to a boolean value\")) { log.error(\"Fix condition {} to return boolean\", condition); throw new IllegalArgumentException(\"Non-boolean condition: \" + condition, e); } throw e; }","preventionTips":["Always write rule conditions as comparisons (x > 0, x == 'Y')","Initialize variables referenced by conditions with boolean-friendly defaults","Test conditions with real variable snapshots before deployment"],"tags":["cmmn","expression","condition","flowable"],"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"}