flowable/flowable-engine · error · FlowableException

DMN decision with key ${externalRef} did not hit any rules f

Error message

DMN decision with key ${externalRef} did not hit any rules for the provided input. For ${planItemInstanceEntity}

What it means

When a decision table executes successfully but matches no rules, the result is empty. If the decision table's throw-error flag (fallthrough/error field) is the literal string 'true', the engine treats 'no rules hit' as an error and throws this FlowableException. This is an explicit model-driven configuration, not an engine malfunction.

Source

Thrown at modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/behavior/impl/DecisionTaskActivityBehavior.java:126

                    .parentDeploymentId(CaseDefinitionUtil.getDefinitionDeploymentId(planItemInstanceEntity.getCaseDefinitionId(), cmmnEngineConfiguration));
        }

        DecisionExecutionAuditContainer decisionExecutionAuditContainer = executeDecisionBuilder.executeWithAuditTrail();

        if (decisionExecutionAuditContainer == null) {
            throw new FlowableException("DMN decision with key " + externalRef + " was not executed. For " + planItemInstanceEntity);
        }
        
        if (decisionExecutionAuditContainer.isFailed()) {
            throw new FlowableException("DMN decision with key " + externalRef + " execution failed. For " + planItemInstanceEntity,
                    decisionExecutionAuditContainer.getException());
        }

        /* Throw error if there were no rules hit when the flag indicates to do this. */
        String throwErrorFieldValue = getFieldString(EXPRESSION_DECISION_TABLE_THROW_ERROR_FLAG);
        if (decisionExecutionAuditContainer.getDecisionResult().isEmpty() && throwErrorFieldValue != null) {
            if ("true".equalsIgnoreCase(throwErrorFieldValue)) {
                throw new FlowableException("DMN decision with key " + externalRef + " did not hit any rules for the provided input. For " + planItemInstanceEntity);
            
            } else if (!"false".equalsIgnoreCase(throwErrorFieldValue)) {
                Expression expression = CommandContextUtil.getExpressionManager(commandContext).createExpression(throwErrorFieldValue);
                Object expressionValue = expression.getValue(planItemInstanceEntity);
                
                if (expressionValue instanceof Boolean && ((Boolean) expressionValue)) {
                    throw new FlowableException("DMN decision with key " + externalRef + " did not hit any rules for the provided input. For " + planItemInstanceEntity);
                }
            }
        }

        if (cmmnEngineConfiguration.getDecisionTableVariableManager() != null) {
            if (decisionExecutionAuditContainer instanceof DecisionServiceExecutionAuditContainer decisionServiceExecutionAuditContainer) {
                cmmnEngineConfiguration.getDecisionTableVariableManager().setDecisionServiceVariablesOnPlanItemInstance(decisionServiceExecutionAuditContainer.getDecisionServiceResult(),
                    externalRef, planItemInstanceEntity, cmmnEngineConfiguration.getObjectMapper(), decisionExecutionAuditContainer.isMultipleResults());
            } else {
                cmmnEngineConfiguration.getDecisionTableVariableManager().setVariablesOnPlanItemInstance(decisionExecutionAuditContainer.getDecisionResult(),
                    externalRef, planItemInstanceEntity, cmmnEngineConfiguration.getObjectMapper(), decisionExecutionAuditContainer.isMultipleResults());

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Add a catch-all/default rule row to the decision table that matches any input and produces a default output.
  2. Loosen the input conditions or add rules covering the actual input values seen in production.
  3. If empty results are acceptable, remove the throw-error field or set it to 'false' in the decision task configuration.
  4. Handle FlowableException around case execution and route the case to a compensation/error plan item.

Example fix

<!-- before: no default rule, throw error enabled -->
<decisionTable throw="true"> ... rows only for amount &gt; 0 ... </decisionTable>
<!-- after: acceptable empty result -->
<decisionTable throw="false"> ... </decisionTable>
<!-- or add a default rule row matching all inputs -->
Defensive patterns

Strategy: try-catch

Validate before calling

// none: emptiness of decision result is only knowable after execution

Try / catch

try {
    caseRuntimeService.triggerPlanItemInstance(planItemId);
} catch (FlowableException e) {
    if (e.getMessage().contains("did not hit any rules")) {
        // route to manual review plan item
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: decisionExecutionAuditContainer.getDecisionResult().isEmpty() and getFieldString(EXPRESSION_DECISION_TABLE_THROW_ERROR_FLAG) equals 'true' (case-insensitive).

Common situations: Decision table hit policy/input conditions too strict so no row matches, combined with the throw-error flag set to true; input values outside expected ranges (e.g. amount null or negative) so no rule fires.

Understand the failure class

Background: EmptyResultError / "no results found": when an API or scraper succeeds but returns zero rows — this error's family across 9 libraries.

Related errors


AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11). Data as JSON: /api/errors/c154f438c789307b. Report an issue: GitHub.