flowable/flowable-engine · error · ActivitiException
condition expression returns non-Boolean
Error message
condition expression returns non-Boolean (sequenceFlowId: ${sequenceFlowId}): ${result} (${result.getClass().getName()}) What it means
After evaluation, UelExpressionCondition type-checks the result: a sequence-flow condition must yield a Boolean. Any non-Boolean value (String, Integer, etc.) throws ActivitiException naming the flow id, the value and its class.
Solutions
- Rewrite the condition to a boolean expression, e.g. ${status == 'active'} instead of ${status}
- Make the invoked bean method return boolean/Boolean
- Declare the variable type properly and use comparison operators in the condition
Example fix
// before
<conditionExpression xsi:type="tFormalExpression">${status}</conditionExpression>
// after
<conditionExpression xsi:type="tFormalExpression">${status == 'active'}</conditionExpression> Defensive patterns
Strategy: validation
Validate before calling
Object r = expression.getValue(execution);
if (!(r instanceof Boolean)) {
throw new IllegalArgumentException("Condition must be Boolean, got: "
+ (r == null ? "null" : r.getClass().getName()));
} Type guard
boolean isBooleanCondition(Object r) {
return r instanceof Boolean;
} Try / catch
try {
condition.evaluate(execution, sequenceFlowId);
} catch (ActivitiException e) {
if (e.getMessage() != null && e.getMessage().contains("non-Boolean")) {
// rewrite expression with comparison operator
}
} Prevention
- Use comparison operators (==, !=, <) in gateway conditions rather than raw variable values
- Type condition-producing bean methods as boolean
- Review BPMN conditions after engine migrations for typing changes
When it happens
Trigger: A conditionExpression returning a truthy non-boolean, e.g. ${status} where status is "active", or a numeric/string expression used as a gateway condition.
Common situations: Migrating from engines/versions with looser condition typing; using string comparisons that return the string rather than a comparison; calling methods that return Object.
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
- condition expression returns null
- Cannot coerce property to array index:
- condition expression returns non-Boolean (elementId: " +…
- condition script returns non-Boolean: () for
- condition script returns non-Boolean
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/f8913f095533f28c.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable5-engine/src/main/java/org/activiti/engine/impl/el/UelExpressionCondition.java:59
@Override
public boolean evaluate(String sequenceFlowId, DelegateExecution execution) {
String conditionExpression = null;
if (Context.getProcessEngineConfiguration().isEnableProcessDefinitionInfoCache()) {
ObjectNode elementProperties = Context.getBpmnOverrideElementProperties(sequenceFlowId, execution.getProcessDefinitionId());
conditionExpression = getActiveValue(initialConditionExpression, DynamicBpmnConstants.SEQUENCE_FLOW_CONDITION, elementProperties);
} else {
conditionExpression = initialConditionExpression;
}
Expression expression = Context.getProcessEngineConfiguration().getExpressionManager().createExpression(conditionExpression);
Object result = expression.getValue(execution);
if (result == null) {
throw new ActivitiException("condition expression returns null (sequenceFlowId: " + sequenceFlowId + ")" );
}
if (!(result instanceof Boolean)) {
throw new ActivitiException("condition expression returns non-Boolean (sequenceFlowId: " + sequenceFlowId + "): " + result + " (" + result.getClass().getName() + ")");
}
return (Boolean) result;
}
protected String getActiveValue(String originalValue, String propertyName, ObjectNode elementProperties) {
String activeValue = originalValue;
if (elementProperties != null) {
JsonNode overrideValueNode = elementProperties.get(propertyName);
if (overrideValueNode != null) {
if (overrideValueNode.isNull()) {
activeValue = null;
} else {
activeValue = overrideValueNode.asString();
}
}
}
return activeValue;
}View on GitHub (pinned to d6d39ce1c6)