{"record":{"id":"e515173e31238ea7","repo":"flowable/flowable-engine","slug":"category-expression-does-not-resolve-to-a-string","errorCode":null,"errorMessage":"Category expression does not resolve to a string: ","messagePattern":"Category 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":372,"sourceCode":"\n    protected void handleCategory(PlanItemInstanceEntity planItemInstanceEntity, ExpressionManager expressionManager,\n            TaskEntity taskEntity, CreateHumanTaskBeforeContext beforeContext, MigrationContext migrationContext) {\n        \n        String categoryStringValue = null;\n        if (migrationContext != null && migrationContext.getCategory() != null) {\n            categoryStringValue = migrationContext.getCategory();\n            \n        } else if (StringUtils.isNotEmpty(beforeContext.getCategory())) {\n            categoryStringValue = beforeContext.getCategory();\n        }\n        \n        if (StringUtils.isNotEmpty(categoryStringValue)) {\n            final Object category = expressionManager.createExpression(categoryStringValue).getValue(planItemInstanceEntity);\n            if (category != null) {\n                if (category instanceof String) {\n                    taskEntity.setCategory((String) category);\n                } else {\n                    throw new FlowableIllegalArgumentException(\"Category expression does not resolve to a string: \" + beforeContext.getCategory());\n                }\n            }\n        }\n    }\n\n    @SuppressWarnings({ \"unchecked\", \"rawtypes\" })\n    protected void handleCandidateUsers(CommandContext commandContext, PlanItemInstanceEntity planItemInstanceEntity,\n            ExpressionManager expressionManager, TaskEntity taskEntity, CreateHumanTaskBeforeContext beforeContext, MigrationContext migrationContext) {\n        \n        CmmnEngineConfiguration cmmnEngineConfiguration = CommandContextUtil.getCmmnEngineConfiguration(commandContext);\n        \n        List<String> candidateUsers = null;\n        if (migrationContext != null && migrationContext.getCandidateUsers() != null) {\n            candidateUsers = migrationContext.getCandidateUsers();\n            \n        } else if (beforeContext.getCandidateUsers() != null) {\n            candidateUsers = beforeContext.getCandidateUsers();\n        }","sourceCodeStart":354,"sourceCodeEnd":390,"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#L354-L390","documentation":"The task category attribute is an expression that resolved to a non-null object which is not a String. Flowable stores the category as a plain String on the task entity, so any other type triggers FlowableIllegalArgumentException showing the original expression.","triggerScenarios":"HumanTaskActivityBehavior.execute -> handleCategory, when the category expression's value is neither null nor a String.","commonSituations":"Category expression bound to an enum constant (e.g. ${TaskCategory.FINANCE}); variable holding a numeric category id; a bean method returning a Category object instead of its name.","solutions":["Make the expression resolve to a String, e.g. ${category.name()} for an enum.","Store the category variable as a String when created.","Reference the specific String field of the object: ${taskMeta.categoryName}.","Catch FlowableIllegalArgumentException during execution and log beforeContext.getCategory() to locate the bad expression."],"exampleFix":"// before\n<cmmn:task category=\"${taskCategory}\"></cmmn:task>\n// after\n<cmmn:task category=\"${taskCategory.name()}\"></cmmn:task>","handlingStrategy":"type-guard","validationCode":"Object c = expressionManager.createExpression(catExpr).getValue(scope);\nif (c != null && !(c instanceof String)) throw new IllegalArgumentException(\"category must resolve to String, got \" + c.getClass());","typeGuard":"boolean isValidCategory(Object v) { return v == null || v instanceof String; }","tryCatchPattern":"try {\n    // execute human task\n} catch (FlowableIllegalArgumentException e) {\n    if (e.getMessage().startsWith(\"Category expression\")) {\n        log.error(\"Category expression '{}' must yield a String\", e.getMessage());\n    }\n}","preventionTips":["Use enum .name() when referencing enum constants in expressions","Store categories as String variables","Keep the category vocabulary defined as plain strings in config"],"tags":["cmmn","expression","category","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"}