flowable/flowable-engine · error · FlowableIllegalArgumentException

decisionTableReferenceKey expression resolves to an empty va

Error message

decisionTableReferenceKey expression resolves to an empty value: ${decisionKeyValue}

What it means

The decisionTableReferenceKey expression resolved successfully but produced null or an empty string. Flowable requires a concrete decision key to look up the DMN decision, so it throws FlowableIllegalArgumentException. This differs from error 2051: the type is fine, the value is empty.

Source

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

        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);
        }

        String finalDecisionKeyValue = null;
        Object decisionKeyValue = expressionManager.createExpression(activeDecisionKey).getValue(execution);
        if (decisionKeyValue != null) {
            if (decisionKeyValue instanceof String) {
                finalDecisionKeyValue = (String) decisionKeyValue;
            } else {
                throw new FlowableIllegalArgumentException("decisionTableReferenceKey expression does not resolve to a string: " + decisionKeyValue);
            }
        }

        if (finalDecisionKeyValue == null || finalDecisionKeyValue.length() == 0) {
            throw new FlowableIllegalArgumentException("decisionTableReferenceKey expression resolves to an empty value: " + decisionKeyValue);
        }

        DmnDecisionService ruleService = CommandContextUtil.getDmnRuleService();

        ExecuteDecisionBuilder executeDecisionBuilder = ruleService.createExecuteDecisionBuilder()
            .decisionKey(finalDecisionKeyValue)
            .instanceId(execution.getProcessInstanceId())
            .executionId(execution.getId())
            .activityId(task.getId())
            .variables(execution.getVariables())
            .tenantId(execution.getTenantId());

        applyFallbackToDefaultTenant(execution, executeDecisionBuilder);
        applyParentDeployment(execution, executeDecisionBuilder, processEngineConfiguration);

        DecisionExecutionAuditContainer decisionExecutionAuditContainer = executeDecisionBuilder.executeWithAuditTrail();

        if (decisionExecutionAuditContainer.isFailed()) {

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Set the decision-key variable before the DMN task executes, or start the process with it: runtimeService.startProcessInstanceByKey(proc, vars with decisionKeyVar)
  2. Correct the variable name in the expression (check case) so it points at an existing variable
  3. Add a default value or guard task/condition that fails fast if the variable is missing
  4. Reorder the flow so any service task that computes the key runs before the DMN task

Example fix

// before
Map<String, Object> vars = new HashMap<>(); // decisionKeyVar never set
runtimeService.startProcessInstanceByKey("myProcess", vars);
// after
Map<String, Object> vars = new HashMap<>();
vars.put("decisionKeyVar", "myDecisionTable");
runtimeService.startProcessInstanceByKey("myProcess", vars);
Defensive patterns

Strategy: validation

Validate before calling

Object key = execution.getVariable("decisionKeyVar");
if (key == null || key.toString().isEmpty()) {
    throw new IllegalStateException("Decision key variable must be set and non-empty before the DMN task");
}

Type guard

static boolean isPresent(String s) { return s != null && !s.isEmpty(); }

Try / catch

try {
    runtimeService.startProcessInstanceByKey("myProcess", vars);
} catch (FlowableIllegalArgumentException e) {
    if (e.getMessage().contains("resolves to an empty value")) {
        // restart with explicit decision key variable
    } else throw e;
}

Prevention

When it happens

Trigger: The expression references a process variable that is null or "" at the time the DMN task executes (variable never set, removed, or set after the task runs); the expression returns an empty result of an EL evaluation (e.g. ${emptyVar}).

Common situations: Asynchronous boundary or parallel branch where the variable is set in another path that hasn't completed; typo in the variable name inside the expression so it silently resolves to null; caller forgot to pass the decision key when starting the process instance.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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