flowable/flowable-engine · error · ActivitiIllegalArgumentException

Expression did not resolve to a string or collection of…

Error message

Expression did not resolve to a string or collection of strings

What it means

Thrown in handleAssignments when a candidateGroups (activiti:candidateGroups) expression resolves to a non-null value that is neither a String nor a Collection. The engine accepts a delimited string of group names or a Collection of them and rejects any other type as ActivitiIllegalArgumentException.

Solutions

  1. Ensure the expression resolves to a String of group names (comma/semicolon separated) e.g. ${groupsString}
  2. Ensure the expression resolves to a java.util.Collection of Strings; convert arrays with Arrays.asList(...) in the bean
  3. Fix the variable type set upstream so it matches String or Collection<String>
  4. Add a conversion in the referenced bean getter to return List<String>

Example fix

// before (bean returns String[])
public String[] getCandidateGroups() { ... }
// after
public List<String> getCandidateGroups() { return Arrays.asList(...); }
Defensive patterns

Strategy: validation

Validate before calling

Object v = execution.getVariable("groupNames");
if (v != null && !(v instanceof String) && !(v instanceof Collection)) {
    throw new IllegalArgumentException("groupNames must be String or Collection<String>");
}

Type guard

boolean ok(Object v) { return v == null || v instanceof String || v instanceof Collection; }

Try / catch

try {
    taskService.complete(taskId);
} catch (ActivitiIllegalArgumentException e) {
    if (e.getMessage().contains("did not resolve to a string or collection")) { /* fix variable type */ }
    throw e;
}

Prevention

When it happens

Trigger: Executing a user task whose candidateGroups expression (e.g. ${groupNames}) evaluates to an Integer, Boolean, Map, or other non-String/non-Collection object.

Common situations: Referencing a numeric variable in candidateGroups; a bean method returning Set wrapped differently or an array (arrays are not Collections); wrong variable assigned in a previous service task.

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/8a34b5aa1f6554ad. Report an issue: GitHub.

Appendix: source

Thrown at modules/flowable5-engine/src/main/java/org/activiti/engine/impl/bpmn/behavior/UserTaskActivityBehavior.java:269

        if (ownerExpression != null) {
            Object ownerExpressionValue = ownerExpression.getValue(execution);
            String ownerValue = null;
            if (ownerExpressionValue != null) {
                ownerValue = ownerExpressionValue.toString();
            }
            task.setOwner(ownerValue);
        }

        if (candidateGroupExpressions != null && !candidateGroupExpressions.isEmpty()) {
            for (Expression groupIdExpr : candidateGroupExpressions) {
                Object value = groupIdExpr.getValue(execution);
                if (value instanceof String) {
                    List<String> candidates = extractCandidates((String) value);
                    task.addCandidateGroups(candidates);
                } else if (value instanceof Collection) {
                    task.addCandidateGroups((Collection) value);
                } else {
                    throw new ActivitiIllegalArgumentException("Expression did not resolve to a string or collection of strings");
                }
            }
        }

        if (candidateUserExpressions != null && !candidateUserExpressions.isEmpty()) {
            for (Expression userIdExpr : candidateUserExpressions) {
                Object value = userIdExpr.getValue(execution);
                if (value instanceof String) {
                    List<String> candiates = extractCandidates((String) value);
                    task.addCandidateUsers(candiates);
                } else if (value instanceof Collection) {
                    task.addCandidateUsers((Collection) value);
                } else {
                    throw new ActivitiException("Expression did not resolve to a string or collection of strings");
                }
            }
        }

View on GitHub (pinned to d6d39ce1c6)