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

  1. Make the expression resolve to a String, e.g. ${category.name()} for an enum.
  2. Store the category variable as a String when created.
  3. Reference the specific String field of the object: ${taskMeta.categoryName}.
  4. 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

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


AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11). Data as JSON: /api/errors/e515173e31238ea7. Report an issue: GitHub.