flowable/flowable-engine · error · FlowableIllegalArgumentException

decisionTableReferenceKey expression does not resolve to a s

Error message

decisionTableReferenceKey expression does not resolve to a string: ${decisionKeyValue}

What it means

When the decisionTableReferenceKey field is defined as an expression, Flowable resolves it against the current execution to obtain the decision key. If the expression evaluates to a non-String object (e.g. Integer, List, POJO), the behavior throws FlowableIllegalArgumentException because the decision key must be a String. The underlying decision is never invoked.

Source

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

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

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

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Ensure the variable referenced by the expression is a String, e.g. set execution.setVariable("decisionKeyVar", String.valueOf(rawId)) before the DMN task
  2. Wrap the conversion in the expression if supported, e.g. expression="${someVar.toString()}"
  3. Fix the upstream service/task that writes the variable so it stores a String
  4. Log the variable type at runtime (delegateExecution.getVariable(...).getClass()) to find what is actually stored

Example fix

// before
execution.setVariable("decisionKeyVar", someNumericId);
// after
execution.setVariable("decisionKeyVar", String.valueOf(someNumericId));
Defensive patterns

Strategy: type-guard

Validate before calling

Object v = execution.getVariable("decisionKeyVar");
if (!(v instanceof String) || ((String) v).isEmpty()) {
    throw new IllegalStateException("decisionTableReferenceKey expression must resolve to a non-empty String");
}

Type guard

static boolean isNonEmptyString(Object o) {
    return o instanceof String && !((String) o).isEmpty();
}

Try / catch

try {
    taskService.complete(taskId);
} catch (FlowableIllegalArgumentException e) {
    if (e.getMessage().contains("does not resolve to a string")) {
        // convert variable to String and retry
    } else throw e;
}

Prevention

When it happens

Trigger: decisionTableReferenceKey expression="${someVar}" where someVar holds a non-String value at execution time (e.g. a number 123, a collection, or a bean); an expression returning an enum or Long instead of a String.

Common situations: Passing a process variable populated from a database numeric ID or JSON number into the expression; forgetting to convert the value (String.valueOf / toString) before it reaches the DMN task; expression accidentally pointing at the wrong variable after a refactor.

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/39624cd13dfc83a1. Report an issue: GitHub.