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
- Add a catch-all/default rule row to the decision table that matches any input and produces a default output.
- Loosen the input conditions or add rules covering the actual input values seen in production.
- If empty results are acceptable, remove the throw-error field or set it to 'false' in the decision task configuration.
- 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 > 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
- Add a default/catch-all rule row to decision tables with throw=true
- Only set the throw-error flag when empty results are genuinely invalid
- Test decision tables with boundary input values (null, zero, negatives)
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
- Could not execute decision instance: no dmn service found. F
- Could not execute decision: no externalRef defined for ${pla
- DMN decision with key ${externalRef} was not executed. For $
- DMN decision with key ${externalRef} execution failed. For $
- Dmn engine is not configured
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/c154f438c789307b.
Report an issue: GitHub.