flowable/flowable-engine · error · FlowableException
rule cannot be null
Error message
rule cannot be null
What it means
executeRule guards against a null DecisionRule while iterating a decision table's rules (RuleEngineExecutorImpl.java:164-167), throwing FlowableException("rule cannot be null"). It protects the per-rule evaluation (audit entry, input-expression evaluation) from dereferencing a null rule. The exception bubbles up to evaluateDecisionTable's FlowableException catch, so the audit container is marked failed with this message rather than crashing the whole execute call.
Source
Thrown at modules/flowable-dmn-engine/src/main/java/org/flowable/dmn/engine/impl/RuleEngineExecutorImpl.java:166
// post rule conclusion actions
if (getHitPolicyBehavior(decisionTable.getHitPolicy()) instanceof ComposeDecisionResultBehavior) {
getHitPolicyBehavior(decisionTable.getHitPolicy()).composeDecisionResults(executionContext);
}
} catch (FlowableException ade) {
LOGGER.error("decision table execution failed", ade);
executionContext.getRuleResults().clear();
executionContext.getAuditContainer().setFailedWithException(ade);
executionContext.getAuditContainer().setExceptionMessage(getExceptionMessage(ade));
}
LOGGER.debug("End table evaluation: {}", decisionTable.getId());
}
protected boolean executeRule(DecisionRule rule, ELExecutionContext executionContext) {
if (rule == null) {
throw new FlowableException("rule cannot be null");
}
LOGGER.debug("Start rule {} evaluation", rule.getRuleNumber());
// add audit entry
executionContext.getAuditContainer().addRuleEntry(rule);
boolean conditionResult = false;
// go through conditions
for (RuleInputClauseContainer conditionContainer : rule.getInputEntries()) {
// resetting value
String inputEntryId = conditionContainer.getInputEntry().getId();
conditionResult = false;
try {
// if condition is empty condition or has dash symbol result is TRUEView on GitHub (pinned to d6d39ce1c6)
Solutions
- Sanitize the decision table before execution: remove null elements from decisionTable.getRules().
- Fix the model source — inspect the deployed .dmn XML or the converter/transformer producing the rules list.
- If building tables programmatically, validate each rule is non-null before adding it.
- Handle the failure gracefully: catch FlowableException from execute() and check the audit container's isFailed()/exceptionMessage.
Example fix
// before
table.getRules().add(null); // later throws "rule cannot be null"
// after
for (DecisionRule rule : rules) {
if (rule != null) {
table.getRules().add(rule);
}
} Defensive patterns
Strategy: validation
Validate before calling
List<DecisionRule> rules = ((DecisionTable) decision.getExpression()).getRules();
if (rules.stream().anyMatch(Objects::isNull)) {
throw new IllegalStateException("decision table contains null rules; fix the model source");
} Try / catch
try {
audit = ruleEngineExecutor.execute(decision, ctx);
} catch (FlowableException fe) {
if ("rule cannot be null".equals(fe.getMessage())) {
log.error("Corrupt DMN model: null rule in table", fe);
}
} Prevention
- Reject null rules when constructing tables programmatically.
- Validate generated .dmn XML against the DMN schema before deployment.
- Audit any custom model transformers/migrations that touch the rules list.
When it happens
Trigger: A DecisionTable whose getRules() list contains a null element — typically from a corrupted/hand-edited .dmn model, a custom model-to-model conversion that inserts null entries, or programmatic table construction adding null rules. Called from executeRule via the rules loop in evaluateDecisionTable (via ruleResult path).
Common situations: Generating DMN XML dynamically where a rule template rendered an empty/null entry, custom model transformers or migration code populating the rules list incorrectly, or reflective calls to executeRule(null, ctx).
Related errors
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/18f0faa31c7c3ae9.
Report an issue: GitHub.