flowable/flowable-engine · error · FlowableException

decisionTableReferenceKey is a required field extension for

Error message

decisionTableReferenceKey is a required field extension for the dmn task ${task.getId()} in ${execution}

What it means

A DMN (decision) task in a BPMN process must declare which decision table to execute via the 'decisionTableReferenceKey' field extension on the task element. When the DmnActivityBehavior executes and cannot find that field extension, or it has neither a string value nor an expression, it aborts the execution with this FlowableException. It is a model/configuration validation error, not a runtime failure of the decision itself.

Source

Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/behavior/DmnActivityBehavior.java:64

    protected static final String EXPRESSION_DECISION_TABLE_REFERENCE_KEY = "decisionTableReferenceKey";
    protected static final String EXPRESSION_DECISION_TABLE_THROW_ERROR_FLAG = "decisionTaskThrowErrorOnNoHits";
    protected static final String EXPRESSION_DECISION_TABLE_FALLBACK_TO_DEFAULT_TENANT = "fallbackToDefaultTenant";
    protected static final String EXPRESSION_DECISION_TABLE_SAME_DEPLOYMENT = "sameDeployment";

    protected Task task;

    public DmnActivityBehavior(Task task) {
        this.task = task;
    }

    @Override
    public void execute(DelegateExecution execution) {
        FieldExtension fieldExtension = DelegateHelper.getFlowElementField(execution, EXPRESSION_DECISION_TABLE_REFERENCE_KEY);
        if (fieldExtension == null || ((fieldExtension.getStringValue() == null || fieldExtension.getStringValue().length() == 0) &&
                (fieldExtension.getExpression() == null || fieldExtension.getExpression().length() == 0))) {

            throw new FlowableException("decisionTableReferenceKey is a required field extension for the dmn task " + task.getId() + " in " + execution);
        }

        String activeDecisionKey = null;
        if (fieldExtension.getExpression() != null && fieldExtension.getExpression().length() > 0) {
            activeDecisionKey = fieldExtension.getExpression();

        } else {
            activeDecisionKey = fieldExtension.getStringValue();
        }

        ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration();
        ExpressionManager expressionManager = processEngineConfiguration.getExpressionManager();

        if (processEngineConfiguration.isEnableProcessDefinitionInfoCache()) {
            ObjectNode taskElementProperties = BpmnOverrideContext.getBpmnOverrideElementProperties(task.getId(), execution.getProcessDefinitionId());
            activeDecisionKey = DynamicPropertyUtil.getActiveValue(activeDecisionKey, DynamicBpmnConstants.DMN_TASK_DECISION_TABLE_KEY, taskElementProperties);
        }

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Add a field extension to the DMN task in the BPMN XML: <extensionElements><flowable:field name="decisionTableReferenceKey" stringValue="myDecisionKey"/></extensionElements>
  2. If the key is dynamic, set the field's expression attribute instead of stringValue, e.g. expression="${decisionKeyVar}"
  3. Verify the field name is exactly 'decisionTableReferenceKey' (case-sensitive) on the task element
  4. Redeploy/restart the process definition after fixing the XML so the new model is used

Example fix

// before (BPMN XML)
<serviceTask id="dmnTask" flowable:type="dmn" />
// after
<serviceTask id="dmnTask" flowable:type="dmn">
  <extensionElements>
    <flowable:field name="decisionTableReferenceKey" stringValue="myDecisionTable" />
  </extensionElements>
</serviceTask>
Defensive patterns

Strategy: validation

Validate before calling

ServiceTask task = (ServiceTask) bpmnModel.getFlowElement(processId, "dmnTask");
FieldExtension ext = (FieldExtension) task.getFields().stream()
    .filter(f -> "decisionTableReferenceKey".equals(f.getFieldName()))
    .findFirst().orElse(null);
boolean valid = ext != null &&
    ((ext.getStringValue() != null && !ext.getStringValue().isEmpty()) ||
     (ext.getExpression() != null && !ext.getExpression().isEmpty()));
if (!valid) throw new IllegalStateException("DMN task missing decisionTableReferenceKey field");

Type guard

boolean hasDecisionKeyField(FlowElement el) {
  if (!(el instanceof ServiceTask)) return false;
  return ((ServiceTask) el).getFields().stream()
    .anyMatch(f -> "decisionTableReferenceKey".equals(f.getFieldName())
      && ((f.getStringValue() != null && !f.getStringValue().isEmpty())
        || (f.getExpression() != null && !f.getExpression().isEmpty())));
}

Prevention

When it happens

Trigger: A <serviceTask flowable:delegateExpression=.../> or DMN task element is deployed without a <flowable:field name="decisionTableReferenceKey"> child that has either a stringValue or an expression; the field name is misspelled so DelegateHelper.getFlowElementField returns null; or the field is present but empty.

Common situations: Hand-editing or generating BPMN XML without the field extension; renaming the field element while migrating from Activiti-style 'decisionTableReferenceKey' config; copying a task template that omitted the field; deploying a model exported by a tool that doesn't serialize Flowable field extensions.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


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