flowable/flowable-engine · error · org.activiti.engine.ActivitiException
condition script returns non-Boolean
Error message
condition script returns non-Boolean: ${result} (${result.getClass().getName()}) What it means
ScriptCondition.evaluate requires the condition script result to be a Boolean. When the script returns a non-Boolean value (String, Number, etc.) it throws ActivitiException including the result and its class name, because sequence-flow conditions must resolve to true/false.
Solutions
- Make the script return an actual boolean (use comparison operators, not string/number results).
- Convert explicitly: e.g. Boolean.parseBoolean(...) inside the script before returning.
- Check which script engine/language is configured — different engines box results differently; normalize in the script.
- For JavaScript, write 'return value === true;' instead of returning a truthy expression.
Example fix
// before (JS condition) return approved; // approved is 1 or "yes" // after return approved === true || approved === "yes";
Defensive patterns
Strategy: validation
Validate before calling
Object r = scriptingEngines.evaluate(expr, language, execution);
if (!(r instanceof Boolean)) {
throw new IllegalStateException("condition must return Boolean, got " + (r == null ? "null" : r.getClass()));
} Type guard
boolean isBooleanResult(Object r) {
return r instanceof Boolean;
} Try / catch
try {
return ScriptCondition.evaluate(...);
} catch (ActivitiException e) {
if (e.getMessage().startsWith("condition script returns non-Boolean")) {
throw new InvalidConditionException(e.getMessage());
}
throw e;
} Prevention
- Return strict booleans from condition scripts; normalize strings/numbers inside the script.
- Use === comparisons in JavaScript conditions, not truthiness.
- Pin the script language attribute so results box consistently.
When it happens
Trigger: A condition script whose evaluation result is not a Boolean instance — e.g. returning a String "true", an Integer flag, or a truthy object from the scripting engine.
Common situations: Scripts returning string comparisons reversed ('true' vs true), JavaScript returning numbers/strings where Java expects Boolean, Groovy returning Integer result of a comparison wrapper, cross-version behavior where a different script engine returns boxed types differently.
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 script returns non-Boolean: () for
- condition script returns null
- Category expression does not resolve to a string
- Class does not implement
- Class or class name is missing
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/2ad6302f883c8852.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable5-engine/src/main/java/org/activiti/engine/impl/scripting/ScriptCondition.java:58
public boolean evaluate(String sequenceFlowId, DelegateExecution execution) {
String conditionExpression = null;
if (Context.getProcessEngineConfiguration().isEnableProcessDefinitionInfoCache()) {
ObjectNode elementProperties = Context.getBpmnOverrideElementProperties(sequenceFlowId, execution.getProcessDefinitionId());
conditionExpression = getActiveValue(expression, DynamicBpmnConstants.SEQUENCE_FLOW_CONDITION, elementProperties);
} else {
conditionExpression = expression;
}
ScriptingEngines scriptingEngines = Context
.getProcessEngineConfiguration()
.getScriptingEngines();
Object result = scriptingEngines.evaluate(conditionExpression, language, execution);
if (result == null) {
throw new ActivitiException("condition script returns null: " + expression);
}
if (!(result instanceof Boolean)) {
throw new ActivitiException("condition script returns non-Boolean: " + 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)