flowable/flowable-engine · error · FlowableIllegalArgumentException
Skip expression does not resolve to a boolean: " + skipExpre
Error message
Skip expression does not resolve to a boolean: " + skipExpression.getExpressionText()
What it means
SkipExpressionUtil.shouldSkipFlowElement throws this FlowableIllegalArgumentException when a skip expression evaluates to a value that is not a Boolean. The expression itself resolves fine, but its result type is wrong — the engine can only branch on true/false.
Source
Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/helper/SkipExpressionUtil.java:89
return ((Boolean) isSkipExpressionEnabled).booleanValue();
} else {
throw new FlowableIllegalArgumentException("Skip expression variable does not resolve to a boolean. " + isSkipExpressionEnabled);
}
}
public static boolean shouldSkipFlowElement(String skipExpressionString, String activityId, DelegateExecution execution, CommandContext commandContext) {
ExpressionManager expressionManager = CommandContextUtil.getProcessEngineConfiguration(commandContext).getExpressionManager();
Expression skipExpression = expressionManager.createExpression(resolveActiveSkipExpression(skipExpressionString, activityId,
execution.getProcessDefinitionId(), commandContext));
Object value = skipExpression.getValue(execution);
if (value instanceof Boolean) {
return ((Boolean) value).booleanValue();
} else {
throw new FlowableIllegalArgumentException("Skip expression does not resolve to a boolean: " + skipExpression.getExpressionText());
}
}
protected static boolean isEnableSkipExpression(ObjectNode globalProperties) {
if (globalProperties != null) {
JsonNode overrideValueNode = globalProperties.get(DynamicBpmnConstants.ENABLE_SKIP_EXPRESSION);
if (overrideValueNode != null && !overrideValueNode.isNull() && "true".equalsIgnoreCase(overrideValueNode.asString())) {
return true;
}
}
return false;
}
protected static String resolveActiveSkipExpression(String skipExpression, String activityId, String processDefinitionId, CommandContext commandContext) {
ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext);
String activeTaskSkipExpression = null;
if (processEngineConfiguration.isEnableProcessDefinitionInfoCache()) {View on GitHub (pinned to d6d39ce1c6)
Solutions
- Make the expression return a boolean: use comparisons or boolean-returning methods, e.g. ${skipFlag == 'yes'} or ${myService.shouldSkip()}.
- Convert the backing variable to Boolean before the activity executes.
- Wrap a non-boolean result in a boolean expression: ${Boolean.parseBoolean(result)}.
- Log/print the expression result in a test (e.g. via a delegate) to see its actual type, then fix the type.
Example fix
// before
<flowable:skipExpression>${skipFlag}</flowable:skipExpression> <!-- skipFlag is String "true" -->
// after
<flowable:skipExpression>${skipFlag == 'true'}</flowable:skipExpression> Defensive patterns
Strategy: validation
Validate before calling
// assert the expression result is boolean before deployment
Object result = expressionManager.createExpression(skipExpr).getValue(mockExecution);
if (!(result instanceof Boolean)) {
throw new IllegalStateException("skipExpression must evaluate to Boolean: " + skipExpr);
} Type guard
static boolean isBooleanExpression(String expr, DelegateExecution exec) {
return SkipExpressionUtil.resolveActiveSkipExpression(expr, exec.getCurrentActivityId(), exec) == null
|| Boolean.class.isAssignableFrom(exprResultType(expr));
} Try / catch
try {
skip = SkipExpressionUtil.shouldSkipFlowElement(skipExpr, activityId, execution, commandContext);
} catch (FlowableIllegalArgumentException e) {
skip = false; // never skip on bad expression
} Prevention
- Write skip expressions as boolean comparisons, not raw variable references.
- Unit-test skip expressions against representative variable types.
- Keep variables feeding skip expressions strictly Boolean.
When it happens
Trigger: A flow element's flowable:skipExpression (e.g. on a service task or a flow) evaluates via the expression manager to a String ("yes"), Integer, or null object instead of a Boolean, and shouldSkipFlowElement then calls skipExpression.getValue(execution).
Common situations: Expressions like ${skipFlag} where skipFlag was set as a String variable; expressions returning method-call results whose return type is not boolean (e.g. ${myService.getStatus()}); miswritten expressions like ${var == 'x' ? 'yes' : 'no'} returning strings; i18n mappings replacing boolean outputs.
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
- Skip expression variable does not resolve to a boolean. " +
- Skip expression does not resolve to a boolean: ${skipExpress
- decisionTableReferenceKey expression does not resolve to a s
- completionCondition '${activeCompletionCondition}' does not
- Custom properties resolver delegate expression " + expressio
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/e2bed447024423c0.
Report an issue: GitHub.