flowable/flowable-engine · error · FlowableException
condition expression returns null (elementId: " + elementId
Error message
condition expression returns null (elementId: " + elementId + ") for " + execution
What it means
Thrown by UelExpressionCondition.evaluate when a condition expression (e.g. a conditional sequence-flow ${...} expression) evaluates to null. The engine requires a definite Boolean verdict to route execution; null gives no routing information, so the engine fails fast instead of guessing a branch.
Source
Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/el/UelExpressionCondition.java:40
* {@link Condition} that resolves an UEL expression at runtime.
*
* @author Joram Barrez
* @author Frederik Heremans
*/
public class UelExpressionCondition implements Condition {
protected Expression expression;
public UelExpressionCondition(Expression expression) {
this.expression = expression;
}
@Override
public boolean evaluate(String elementId, DelegateExecution execution) {
Object result = expression.getValue(execution);
if (result == null) {
throw new FlowableException("condition expression returns null (elementId: " + elementId + ") for " + execution);
}
if (!(result instanceof Boolean)) {
throw new FlowableException("condition expression returns non-Boolean (elementId: " + elementId + "): " + result + " (" + result.getClass().getName() + ") for " + execution);
}
return (Boolean) result;
}
}
View on GitHub (pinned to d6d39ce1c6)
Solutions
- Set the process variable referenced by the condition before execution reaches the conditional flow (runtimeService.setVariable(executionId, ...) or pass it at startProcessInstanceBy...).
- Make the condition null-safe, e.g. ${orderStatus != null && orderStatus == 'OK'}.
- Give the gateway a default flow so unset conditions route predictably (though the null verdict still throws - fixing the variable is required).
- Log/check which variable is missing: parse elementId from the message and inspect the process XML for the conditionExpression on that flow element.
Example fix
// before: expression references missing variable
<conditionExpression xsi:type="tFormalExpression">${order.status == 'APPROVED'}</conditionExpression>
// after: null-safe expression + guaranteed variable
<conditionExpression xsi:type="tFormalExpression">${order != null && order.status == 'APPROVED'}</conditionExpression>
// and in Java: runtimeService.startProcessInstanceByKey("order", vars) with vars.put("order", order) Defensive patterns
Strategy: validation
Validate before calling
// before starting/completing tasks that reach conditional flows
if (vars.get("orderStatus") == null) {
throw new IllegalStateException("orderStatus must be set before gateway");
} Type guard
Object v = execution.getVariable("orderStatus");
if (v instanceof String && ((String) v).isEmpty()) {
throw new IllegalArgumentException("orderStatus empty");
} Try / catch
try {
taskService.complete(taskId, vars);
} catch (FlowableException e) {
if (e.getMessage().contains("condition expression returns null")) {
// set missing variable and retry
}
} Prevention
- Always put process variables required by conditions in the start/complete variable map.
- Write null-safe EL expressions (${x != null && ...}).
- Model default flows on exclusive gateways.
- Add a unit test that walks each conditional flow with expected variable sets.
When it happens
Trigger: Calling evaluate(elementId, execution) where expression.getValue(execution) returns null. Typical API path: process runtime reaching an inclusive/exclusive gateway or conditional flow whose conditionExpression resolves to nothing - e.g. expression ${orderStatus == 'OK'} where variable orderStatus is unset (juel resolves to null), or an expression returning null explicitly.
Common situations: Process variables not set before the gateway (deployed process expects a variable the caller never passes); typos in variable names inside EL expressions; Spring/CDN bean lookups in expressions resolving to null; migrations where a variable was renamed; returning null from a delegateBean method used in the condition.
Related errors
- condition expression returns non-Boolean (elementId: " + ele
- Error while sending signal for " + eventSubscription + ": no
- Compensating execution not set for " + eventSubscription
- No outgoing sequence flow of the inclusive gateway '${activi
- Not supported to signal this execution
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/8f8bde58b629558a.
Report an issue: GitHub.