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
- Ensure the variable referenced by the expression is a String, e.g. set execution.setVariable("decisionKeyVar", String.valueOf(rawId)) before the DMN task
- Wrap the conversion in the expression if supported, e.g. expression="${someVar.toString()}"
- Fix the upstream service/task that writes the variable so it stores a String
- 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
- Store decision keys as String variables; convert numeric values with String.valueOf at write time
- Type-check the variable in an execution listener before the DMN task
- Avoid binding the expression to variables that can hold arbitrary objects
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
- HitPolicy: %s has aggregation: %s needs output type number
- decisionTableReferenceKey expression resolves to an empty va
- Skip expression does not resolve to a boolean: " + skipExpre
- Unable to resolve formFieldValidationExpression to boolean v
- expected expression ${expression} to resolve to ${type} but
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/39624cd13dfc83a1.
Report an issue: GitHub.