{"record":{"id":"39624cd13dfc83a1","repo":"flowable/flowable-engine","slug":"decisiontablereferencekey-expression-does-not-reso","errorCode":null,"errorMessage":"decisionTableReferenceKey expression does not resolve to a string: ${decisionKeyValue}","messagePattern":"decisionTableReferenceKey expression does not resolve to a string: (.+?)","errorType":"validation","errorClass":"FlowableIllegalArgumentException","httpStatus":null,"severity":"error","filePath":"modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/behavior/DmnActivityBehavior.java","lineNumber":89,"sourceCode":"        } else {\r\n            activeDecisionKey = fieldExtension.getStringValue();\r\n        }\r\n\r\n        ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration();\r\n        ExpressionManager expressionManager = processEngineConfiguration.getExpressionManager();\r\n\r\n        if (processEngineConfiguration.isEnableProcessDefinitionInfoCache()) {\r\n            ObjectNode taskElementProperties = BpmnOverrideContext.getBpmnOverrideElementProperties(task.getId(), execution.getProcessDefinitionId());\r\n            activeDecisionKey = DynamicPropertyUtil.getActiveValue(activeDecisionKey, DynamicBpmnConstants.DMN_TASK_DECISION_TABLE_KEY, taskElementProperties);\r\n        }\r\n\r\n        String finalDecisionKeyValue = null;\r\n        Object decisionKeyValue = expressionManager.createExpression(activeDecisionKey).getValue(execution);\r\n        if (decisionKeyValue != null) {\r\n            if (decisionKeyValue instanceof String) {\r\n                finalDecisionKeyValue = (String) decisionKeyValue;\r\n            } else {\r\n                throw new FlowableIllegalArgumentException(\"decisionTableReferenceKey expression does not resolve to a string: \" + decisionKeyValue);\r\n            }\r\n        }\r\n\r\n        if (finalDecisionKeyValue == null || finalDecisionKeyValue.length() == 0) {\r\n            throw new FlowableIllegalArgumentException(\"decisionTableReferenceKey expression resolves to an empty value: \" + decisionKeyValue);\r\n        }\r\n\r\n        DmnDecisionService ruleService = CommandContextUtil.getDmnRuleService();\r\n\r\n        ExecuteDecisionBuilder executeDecisionBuilder = ruleService.createExecuteDecisionBuilder()\r\n            .decisionKey(finalDecisionKeyValue)\r\n            .instanceId(execution.getProcessInstanceId())\r\n            .executionId(execution.getId())\r\n            .activityId(task.getId())\r\n            .variables(execution.getVariables())\r\n            .tenantId(execution.getTenantId());\r\n\r\n        applyFallbackToDefaultTenant(execution, executeDecisionBuilder);\r","sourceCodeStart":71,"sourceCodeEnd":107,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/behavior/DmnActivityBehavior.java#L71-L107","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nexecution.setVariable(\"decisionKeyVar\", someNumericId);\n// after\nexecution.setVariable(\"decisionKeyVar\", String.valueOf(someNumericId));","handlingStrategy":"type-guard","validationCode":"Object v = execution.getVariable(\"decisionKeyVar\");\nif (!(v instanceof String) || ((String) v).isEmpty()) {\n    throw new IllegalStateException(\"decisionTableReferenceKey expression must resolve to a non-empty String\");\n}","typeGuard":"static boolean isNonEmptyString(Object o) {\n    return o instanceof String && !((String) o).isEmpty();\n}","tryCatchPattern":"try {\n    taskService.complete(taskId);\n} catch (FlowableIllegalArgumentException e) {\n    if (e.getMessage().contains(\"does not resolve to a string\")) {\n        // convert variable to String and retry\n    } else throw e;\n}","preventionTips":["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"],"tags":["flowable","dmn","expression","type-mismatch"],"backgroundTag":"type-mismatch","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}