flowable/flowable-engine · error · FlowableException

HitPolicy: %s has aggregation: %s needs output type number

Error message

HitPolicy: %s has aggregation: %s needs output type number

What it means

A COLLECT hit policy with an aggregation must aggregate over a single output whose type is 'number'. The sanity check rejects tables whose sole output column declares a different typeRef because the aggregator result would be untyped or wrong.

Source

Thrown at modules/flowable-dmn-engine/src/main/java/org/flowable/dmn/engine/impl/RuleEngineExecutorImpl.java:329

        if (hitPolicyBehavior == null) {
            String hitPolicyBehaviorNotFoundMessage = String.format("HitPolicy behavior: %s not configured", hitPolicy.getValue());

            LOGGER.error(hitPolicyBehaviorNotFoundMessage);

            throw new FlowableException(hitPolicyBehaviorNotFoundMessage);
        }

        return hitPolicyBehavior;
    }

    protected void sanityCheckDecisionTable(DecisionTable decisionTable) {
        if (decisionTable.getHitPolicy() == HitPolicy.COLLECT && decisionTable.getAggregation() != null && decisionTable.getOutputs() != null) {
            if (decisionTable.getOutputs().size() > 1) {
                throw new FlowableException(String.format("HitPolicy: %s has aggregation: %s and multiple outputs. This is not supported", decisionTable.getHitPolicy(), decisionTable.getAggregation()));
            }
            if (!"number".equals(decisionTable.getOutputs().get(0).getTypeRef())) {
                throw new FlowableException(String.format("HitPolicy: %s has aggregation: %s needs output type number", decisionTable.getHitPolicy(), decisionTable.getAggregation()));
            }
        }
    }

    @Override
    public Map<String, AbstractHitPolicy> getHitPolicyBehaviors() {
        return hitPolicyBehaviors;
    }

    @Override
    public void setHitPolicyBehaviors(Map<String, AbstractHitPolicy> hitPolicyBehaviors) {
        this.hitPolicyBehaviors = hitPolicyBehaviors;
    }

    @Override
    public ExpressionManager getExpressionManager() {
        return expressionManager;
    }

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Set the output column typeRef to "number" in the DMN XML
  2. If the aggregated value is not numeric, remove the aggregation or change the hit policy
  3. Re-validate/redeploy the corrected decision table

Example fix

// before
<outputEntry typeRef="string">...</outputEntry>
// after
<outputEntry typeRef="number">...</outputEntry>
Defensive patterns

Strategy: validation

Validate before calling

if (dt.getHitPolicy() == HitPolicy.COLLECT && dt.getAggregation() != null && dt.getOutputs() != null && dt.getOutputs().size() == 1) {
  if (!"number".equals(dt.getOutputs().get(0).getTypeRef())) throw new IllegalArgumentException("aggregation output must be number");
}

Prevention

When it happens

Trigger: execute -> sanityCheckDecisionTable finds COLLECT + aggregation, one output, and its typeRef is not "number" (e.g. string, boolean, date).

Common situations: Authoring mistake in the DMN editor where the output column type was left as string or not set; tables converted from tools that don't enforce numeric aggregation.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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