{"record":{"id":"f0ed9e8c49800384","repo":"flowable/flowable-engine","slug":"documentation-expression-does-not-resolve-to-a-str","errorCode":null,"errorMessage":"documentation expression does not resolve to a string: ","messagePattern":"documentation expression does not resolve to a string: ","errorType":"exception","errorClass":"FlowableIllegalArgumentException","httpStatus":null,"severity":"error","filePath":"modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/behavior/impl/HumanTaskActivityBehavior.java","lineNumber":212,"sourceCode":"                if (name instanceof String) {\n                    taskEntity.setName((String) name);\n                } else {\n                    throw new FlowableIllegalArgumentException(\"name expression does not resolve to a string: \" + beforeContext.getName());\n                }\n            }\n        }\n    }\n\n    protected void handleTaskDescription(PlanItemInstanceEntity planItemInstanceEntity, ExpressionManager expressionManager, \n                    TaskEntity taskEntity, CreateHumanTaskBeforeContext beforeContext) {\n        \n        if (StringUtils.isNotEmpty(beforeContext.getDescription())) {\n            Object description = expressionManager.createExpression(beforeContext.getDescription()).getValue(planItemInstanceEntity);\n            if (description != null) {\n                if (description instanceof String) {\n                    taskEntity.setDescription((String) description);\n                } else {\n                    throw new FlowableIllegalArgumentException(\"documentation expression does not resolve to a string: \" + beforeContext.getDescription());\n                }\n            }\n        }\n    }\n\n    protected void handleAssignee(PlanItemInstanceEntity planItemInstanceEntity, TaskService taskService,\n            ExpressionManager expressionManager, TaskEntity taskEntity, PlanItemInstanceEntityManager planItemInstanceEntityManager,\n            CreateHumanTaskBeforeContext beforeContext, MigrationContext migrationContext) {\n        \n        String assigneeStringValue = null;\n        if (migrationContext != null && migrationContext.getAssignee() != null) {\n            assigneeStringValue = migrationContext.getAssignee();\n            \n        } else if (StringUtils.isNotEmpty(beforeContext.getAssignee())) {\n            assigneeStringValue = beforeContext.getAssignee();\n        }\n        \n        if (StringUtils.isNotEmpty(assigneeStringValue)) {","sourceCodeStart":194,"sourceCodeEnd":230,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/behavior/impl/HumanTaskActivityBehavior.java#L194-L230","documentation":"The cmmn:documentation / task description attribute was written as an expression (e.g. ${...}). When Flowable evaluates it during human task creation, the result must be a String; any other type (Integer, Date, etc.) is rejected by throwing FlowableIllegalArgumentException so a wrong-typed value is not silently stored on the task.","triggerScenarios":"HumanTaskActivityBehavior.execute -> handleTaskDescription, when the plan item's TaskDefinition has a non-empty description whose expression evaluates to a non-null, non-String object.","commonSituations":"Description expression like ${someDate} or ${count} resolving to a Date/Number; bean method returning non-String; typo pointing at the wrong variable; copy-pasting a description expression that worked for a form key into the description field.","solutions":["Make the description expression resolve to a String, e.g. wrap it: ${toString(myVar)} or use a String variable.","Fix the referenced variable/bean so its value is a String (or convert it at the source).","If a static description is intended, remove the ${...} expression and write plain text in the documentation element.","Catch FlowableIllegalArgumentException around the plan item start and log the offending expression for correction."],"exampleFix":"// before (CMMN XML)\n<cmmn:documentation>${dueDate}</cmmn:documentation>\n// after\n<cmmn:documentation>${dueDate.toString()}</cmmn:documentation>","handlingStrategy":"type-guard","validationCode":"Object desc = expressionManager.createExpression(descStr).getValue(scope);\nif (desc != null && !(desc instanceof String)) throw new IllegalArgumentException(\"Description must resolve to String, got \" + desc.getClass());","typeGuard":"boolean isValidDescription(Object v) { return v == null || v instanceof String; }","tryCatchPattern":"try {\n    runtimeService.createPlanItemInstanceQuery()...; // start/execute plan item\n} catch (FlowableIllegalArgumentException e) {\n    if (e.getMessage().startsWith(\"documentation expression\")) {\n        log.error(\"Bad description expression: {}\", e.getMessage());\n    }\n}","preventionTips":["Only use ${...} in documentation when the variable is guaranteed to be a String","Convert non-String values at variable-creation time with toString()","Unit-test CMMN models with representative expression values before deploying"],"tags":["cmmn","expression","type-mismatch","human-task"],"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"}