flowable/flowable-engine · error · FlowableIllegalArgumentException
Category expression does not resolve to a string:
Error message
Category expression does not resolve to a string:
What it means
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.
Source
Thrown at modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/behavior/impl/HumanTaskActivityBehavior.java:372
protected void handleCategory(PlanItemInstanceEntity planItemInstanceEntity, ExpressionManager expressionManager,
TaskEntity taskEntity, CreateHumanTaskBeforeContext beforeContext, MigrationContext migrationContext) {
String categoryStringValue = null;
if (migrationContext != null && migrationContext.getCategory() != null) {
categoryStringValue = migrationContext.getCategory();
} else if (StringUtils.isNotEmpty(beforeContext.getCategory())) {
categoryStringValue = beforeContext.getCategory();
}
if (StringUtils.isNotEmpty(categoryStringValue)) {
final Object category = expressionManager.createExpression(categoryStringValue).getValue(planItemInstanceEntity);
if (category != null) {
if (category instanceof String) {
taskEntity.setCategory((String) category);
} else {
throw new FlowableIllegalArgumentException("Category expression does not resolve to a string: " + beforeContext.getCategory());
}
}
}
}
@SuppressWarnings({ "unchecked", "rawtypes" })
protected void handleCandidateUsers(CommandContext commandContext, PlanItemInstanceEntity planItemInstanceEntity,
ExpressionManager expressionManager, TaskEntity taskEntity, CreateHumanTaskBeforeContext beforeContext, MigrationContext migrationContext) {
CmmnEngineConfiguration cmmnEngineConfiguration = CommandContextUtil.getCmmnEngineConfiguration(commandContext);
List<String> candidateUsers = null;
if (migrationContext != null && migrationContext.getCandidateUsers() != null) {
candidateUsers = migrationContext.getCandidateUsers();
} else if (beforeContext.getCandidateUsers() != null) {
candidateUsers = beforeContext.getCandidateUsers();
}View on GitHub (pinned to d6d39ce1c6)
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.
Example fix
// before
<cmmn:task category="${taskCategory}"></cmmn:task>
// after
<cmmn:task category="${taskCategory.name()}"></cmmn:task> Defensive patterns
Strategy: type-guard
Validate before calling
Object c = expressionManager.createExpression(catExpr).getValue(scope);
if (c != null && !(c instanceof String)) throw new IllegalArgumentException("category must resolve to String, got " + c.getClass()); Type guard
boolean isValidCategory(Object v) { return v == null || v instanceof String; } Try / catch
try {
// execute human task
} catch (FlowableIllegalArgumentException e) {
if (e.getMessage().startsWith("Category expression")) {
log.error("Category expression '{}' must yield a String", e.getMessage());
}
} Prevention
- Use enum .name() when referencing enum constants in expressions
- Store categories as String variables
- Keep the category vocabulary defined as plain strings in config
When it happens
Trigger: HumanTaskActivityBehavior.execute -> handleCategory, when the category expression's value is neither null nor a String.
Common situations: 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.
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
- name expression does not resolve to a string:
- documentation expression does not resolve to a string:
- Priority expression does not resolve to a number:
- FormKey expression does not resolve to a string:
- Due date expression does not resolve to a Date, Instant, Loc
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/e515173e31238ea7.
Report an issue: GitHub.