flowable/flowable-engine · error · FlowableException
HitPolicy UNIQUE violated.
Error message
HitPolicy UNIQUE violated.
What it means
HitPolicyUnique.evaluateRuleValidity enforces DMN's UNIQUE hit policy: at most one rule may be valid. When another already-registered valid rule is found, strict mode throws FlowableException('HitPolicy UNIQUE violated.') while also marking both rules' exception messages; in lenient mode it records a validation message on both rules and continues.
Solutions
- Rewrite rule conditions to be mutually exclusive (tighten bounds, add disambiguating conditions)
- Change the hit policy to FIRST or ANY if multiple matching is acceptable
- Use the audit messages naming the two conflicting rules and adjust those rows
- Validate the table with representative input data before deployment
Example fix
// before (UNIQUE policy with overlapping rules) <hitPolicy>UNIQUE</hitPolicy> <!-- rule 1: age > 18; rule 2: income < 5000 (also true for age > 18) --> // after (disambiguate) <!-- rule 2: age > 18 && income < 5000 && age <= 30 -->
Defensive patterns
Strategy: validation
Validate before calling
// ensure no two rules overlap for the planned input domain assert distinctPairs(table.getRules()).noneMatch((a, b) -> overlaps(a, b)) : "UNIQUE policy would be violated";
Try / catch
try { decisionTable.execute(input); } catch (FlowableException e) { if (e.getMessage().equals("HitPolicy UNIQUE violated.")) { /* inspect audit for conflicting rule ids */ } throw e; } Prevention
- Keep UNIQUE-policy conditions mutually exclusive
- Review audit validation messages naming conflicting rules
- Consider ANY or FIRST if multiple hits are acceptable
When it happens
Trigger: Evaluating a decision table with hit policy UNIQUE where two distinct rule numbers both evaluate as valid for the given input.
Common situations: Overlapping rule conditions in a table meant to be mutually exclusive; input data that accidentally satisfies two rows; copy-pasted rules after model edits; using UNIQUE where ANY/FIRST was intended.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- HitPolicy violated.
- HitPolicy: violated; no output values present
- HitPolicy violated; no output values present.
- more than one result
- more than one result in decision:
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/28efe077e8068896.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable-dmn-engine/src/main/java/org/flowable/dmn/engine/impl/hitpolicy/HitPolicyUnique.java:47
*/
public class HitPolicyUnique extends AbstractHitPolicy implements EvaluateRuleValidityBehavior, ComposeDecisionResultBehavior {
@Override
public String getHitPolicyName() {
return HitPolicy.UNIQUE.getValue();
}
@Override
public void evaluateRuleValidity(int ruleNumber, ELExecutionContext executionContext) {
//TODO: not on audit container
for (Map.Entry<Integer, RuleExecutionAuditContainer> entry : executionContext.getAuditContainer().getRuleExecutions().entrySet()) {
if (entry.getKey().equals(ruleNumber) == false && entry.getValue().isValid()) {
String hitPolicyViolatedMessage = String.format("HitPolicy %s violated; at least rule %d and rule %d are valid.", getHitPolicyName(), ruleNumber, entry.getKey());
if (CommandContextUtil.getDmnEngineConfiguration().isStrictMode()) {
executionContext.getAuditContainer().getRuleExecutions().get(ruleNumber).setExceptionMessage(hitPolicyViolatedMessage);
executionContext.getAuditContainer().getRuleExecutions().get(entry.getKey()).setExceptionMessage(hitPolicyViolatedMessage);
throw new FlowableException("HitPolicy UNIQUE violated.");
} else {
executionContext.getAuditContainer().getRuleExecutions().get(ruleNumber).setValidationMessage(hitPolicyViolatedMessage);
executionContext.getAuditContainer().getRuleExecutions().get(entry.getKey()).setValidationMessage(hitPolicyViolatedMessage);
break;
}
}
}
}
@Override
public void composeDecisionResults(ELExecutionContext executionContext) {
List<Map<String, Object>> ruleResults = new ArrayList<>(executionContext.getRuleResults().values());
List<Map<String, Object>> decisionResults;
if (ruleResults.size() > 1 && CommandContextUtil.getDmnEngineConfiguration().isStrictMode() == false) {
Map<String, Object> lastResult = new HashMap<>();
for (Map<String, Object> ruleResult : ruleResults) {View on GitHub (pinned to d6d39ce1c6)