flowable/flowable-engine · error · ActivitiIllegalArgumentException

${collectionExpressionText}' didn't resolve to a Collection

Error message

${collectionExpressionText}' didn't resolve to a Collection

What it means

In resolveNrOfInstances, when a multi-instance activity's loopCardinality is absent and a collectionExpression is configured, the expression is evaluated and the result must be a java.util.Collection. Any non-Collection result (null, String, array, map-like object, Integer) throws this ActivitiIllegalArgumentException naming the expression text.

Source

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

    }

    // required for supporting external subprocesses
    @Override
    public void completed(ActivityExecution execution) throws Exception {
        leave(execution);
    }

    // Helpers //////////////////////////////////////////////////////////////////////

    @SuppressWarnings("rawtypes")
    protected int resolveNrOfInstances(ActivityExecution execution) {
        int nrOfInstances = -1;
        if (loopCardinalityExpression != null) {
            nrOfInstances = resolveLoopCardinality(execution);
        } else if (collectionExpression != null) {
            Object obj = collectionExpression.getValue(execution);
            if (!(obj instanceof Collection)) {
                throw new ActivitiIllegalArgumentException(collectionExpression.getExpressionText() + "' didn't resolve to a Collection");
            }
            nrOfInstances = ((Collection) obj).size();
        } else if (collectionVariable != null) {
            Object obj = execution.getVariable(collectionVariable);
            if (obj == null) {
                throw new ActivitiIllegalArgumentException("Variable " + collectionVariable + " was not found");
            }
            if (!(obj instanceof Collection)) {
                throw new ActivitiIllegalArgumentException("Variable " + collectionVariable + "' is not a Collection");
            }
            nrOfInstances = ((Collection) obj).size();
        } else {
            throw new ActivitiIllegalArgumentException("Couldn't resolve collection expression nor variable reference");
        }
        return nrOfInstances;
    }

    @SuppressWarnings("rawtypes")

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Ensure the expression resolves to a java.util.Collection (List/Set); convert arrays with Arrays.asList(...) before storing.
  2. Initialize the variable to an empty collection instead of leaving it null.
  3. If the data is a String, split it into a List in a delegate or use an expression that returns a List.
  4. Alternatively use loopCardinality or a collectionVariable with a properly typed collection variable.

Example fix

// before
String assignees = "john,jane";
execution.setVariable("assignees", assignees);

// after
List<String> assignees = Arrays.asList("john", "jane");
execution.setVariable("assignees", assignees);
Defensive patterns

Strategy: validation

Validate before calling

// Validate before the process reaches the multi-instance activity
Object value = expression != null ? expression.getValue(execution) : null;
if (!(value instanceof java.util.Collection)) {
    throw new IllegalArgumentException("flowable:collection expression must resolve to a java.util.Collection, got: "
        + (value == null ? "null" : value.getClass().getName()));
}

Type guard

boolean isCollectionForMultiInstance(Object o) {
    return o instanceof java.util.Collection && !((java.util.Collection<?>) o).isEmpty() || o instanceof java.util.Collection;
}

Try / catch

try {
    runtimeService.startProcessInstanceByKey(key, vars);
} catch (org.activiti.engine.ActivitiIllegalArgumentException e) {
    if (e.getMessage() != null && e.getMessage().endsWith("' didn't resolve to a Collection")) {
        // fix the expression/variable to return a java.util.Collection
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: flowable:collection="${myExpression}" on a multi-instance task where the expression resolves to a non-Collection value at runtime (e.g. null variable, a String, an array, or a custom object).

Common situations: Expression returning an array instead of a List; variable never initialized so the expression yields null; passing a comma-separated string expecting it to be treated as a collection; returning a single object from a service/delegate instead of a list; JSON-deserialized objects that are not java.util.Collection.

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/15cb40e1b282e115. Report an issue: GitHub.