flowable/flowable-engine · error · FlowableException
Expression condition ${condition} did not evaluate to a bool
Error message
Expression condition ${condition} did not evaluate to a boolean value with ${variableContainer} What it means
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.
Source
Thrown at modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/util/ExpressionUtil.java:48
import org.flowable.common.engine.api.FlowableIllegalArgumentException;
import org.flowable.common.engine.api.delegate.Expression;
import org.flowable.common.engine.api.variable.VariableContainer;
import org.flowable.common.engine.impl.interceptor.CommandContext;
/**
* @author Joram Barrez
* @author Micha Kiener
*/
public class ExpressionUtil {
public static boolean evaluateBooleanExpression(CommandContext commandContext, VariableContainer variableContainer, String condition) {
Object evaluationResult = evaluateExpression(commandContext, variableContainer, condition);
if (evaluationResult instanceof Boolean) {
return (boolean) evaluationResult;
} else if (evaluationResult instanceof String) {
return "true".equals(((String) evaluationResult).toLowerCase());
} else {
throw new FlowableException("Expression condition " + condition + " did not evaluate to a boolean value with " + variableContainer);
}
}
public static Object evaluateExpression(CommandContext commandContext, VariableContainer variableContainer, String expression) {
Expression exp = CommandContextUtil.getExpressionManager(commandContext).createExpression(expression);
return exp.getValue(variableContainer);
}
public static boolean isRequiredPlanItemInstance(CommandContext commandContext, PlanItemInstanceEntity planItemInstanceEntity) {
PlanItemControl planItemControl = planItemInstanceEntity.getPlanItem().getItemControl();
if (planItemControl != null && planItemControl.getRequiredRule() != null) {
boolean isRequired = true; // Having a required rule means required by default, unless the condition says otherwise
String requiredCondition = planItemControl.getRequiredRule().getCondition();
if (StringUtils.isNotEmpty(requiredCondition)) {
isRequired = evaluateBooleanExpression(commandContext, planItemInstanceEntity, requiredCondition);
}
return isRequired;View on GitHub (pinned to d6d39ce1c6)
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
Example fix
// before
condition="${orderStatus}"
// after
condition="${orderStatus == 'APPROVED'}" Defensive patterns
Strategy: type-guard
Validate before calling
Object result = evaluateExpression(cmdCtx, container, condition);
if (!(result instanceof Boolean) && !(result instanceof String s && ("true".equalsIgnoreCase(s) || "false".equalsIgnoreCase(s)))) {
throw new IllegalStateException("Condition must yield boolean, got: " + result);
} Type guard
boolean isBooleanExpressionResult(Object v) { return v instanceof Boolean || (v instanceof String s && ("true".equalsIgnoreCase(s) || "false".equalsIgnoreCase(s))); } Try / catch
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; } Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
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
- scopeContainer cannot be null
- Unable to resolve formFieldValidationExpression to boolean v
- Unable to resolve formFieldValidationExpression without vari
- Setting variable is not supported for read only delegate exe
- Error converting process reference expression
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/391a335594665d5c.
Report an issue: GitHub.