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 TRUE

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Sanitize the decision table before execution: remove null elements from decisionTable.getRules().
  2. Fix the model source — inspect the deployed .dmn XML or the converter/transformer producing the rules list.
  3. If building tables programmatically, validate each rule is non-null before adding it.
  4. 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

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.