flowable/flowable-engine · error · ActivitiIllegalArgumentException
Category expression does not resolve to a string: %s
Error message
Category expression does not resolve to a string: %s
What it means
Thrown when a BPMN user task declares an activiti:category attribute containing an expression whose evaluation returns a non-null value that is not a String. The engine only accepts String results for the task category and refuses to silently coerce other types. The expression text is included in the message to identify the offending expression.
Source
Thrown at modules/flowable5-engine/src/main/java/org/activiti/engine/impl/bpmn/behavior/UserTaskActivityBehavior.java:194
} catch (NumberFormatException e) {
throw new ActivitiIllegalArgumentException("Priority does not resolve to a number: " + priority, e);
}
} else if (priority instanceof Number) {
task.setPriority(((Number) priority).intValue());
} else {
throw new ActivitiIllegalArgumentException("Priority expression does not resolve to a number: " +
activePriorityExpression.getExpressionText());
}
}
}
if (activeCategoryExpression != null) {
final Object category = activeCategoryExpression.getValue(execution);
if (category != null) {
if (category instanceof String) {
task.setCategory((String) category);
} else {
throw new ActivitiIllegalArgumentException("Category expression does not resolve to a string: " +
activeCategoryExpression.getExpressionText());
}
}
}
if (activeFormKeyExpression != null) {
final Object formKey = activeFormKeyExpression.getValue(execution);
if (formKey != null) {
if (formKey instanceof String) {
task.setFormKey((String) formKey);
} else {
throw new ActivitiIllegalArgumentException("FormKey expression does not resolve to a string: " +
activeFormKeyExpression.getExpressionText());
}
}
}
if (!skipUserTask) {View on GitHub (pinned to d6d39ce1c6)
Solutions
- Make the expression resolve to a String, e.g. use toString() in the expression: ${myObj.categoryAsString}
- Fix the underlying bean/variable so the referenced value is declared as a String (e.g. change the variable type set earlier in the process)
- If the value may be absent, wrap with a null check in the expression so it yields null (which is allowed) instead of a non-String
- Convert numeric/date values explicitly, e.g. ${categoryNumber.toString()} or use a formatting function
Example fix
// before (expression returns Integer)
<userTask id="task1" activiti:category="${categoryNumber}" />
// after (coerce to String)
<userTask id="task1" activiti:category="${categoryNumber.toString()}" /> Defensive patterns
Strategy: validation
Validate before calling
Object v = ((ExecutionEntity) execution).getVariable("categoryVar");
if (v != null && !(v instanceof String)) {
throw new IllegalArgumentException("categoryVar must resolve to String, got " + v.getClass());
} Type guard
if (category instanceof String) { task.setCategory((String) category); } Try / catch
try {
runtimeService.startProcessInstanceByKey(key, vars);
} catch (ActivitiIllegalArgumentException e) {
if (e.getMessage().startsWith("Category expression")) { /* fix variable type */ }
throw e;
} Prevention
- Declare process variables used in string attributes as String
- Use .toString() in expressions that reference non-String beans
- Add a unit test asserting each category expression resolves to a String
When it happens
Trigger: Executing a UserTask whose UserTaskActivityBehavior.execute() evaluates activeCategoryExpression (from the task definition's category expression) and Expression.getValue(execution) returns e.g. an Integer, Boolean, or POJO instead of a String.
Common situations: Setting activiti:category="${myCounter}" where myCounter is an int; pointing the category expression at a Java bean or date object; returning a non-String from a custom bean/DelegateExpression used in the category attribute.
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
- completionCondition '${activeCompletionCondition}' does not
- Due date expression does not resolve to a Date, Instant, Loc
- Priority expression does not resolve to a number: + activeTa
- Skip expression does not resolve to a boolean: " + skipExpre
- Custom properties resolver expression " + expression + " did
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/ef053c5cd4d0494f.
Report an issue: GitHub.