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
- Ensure the expression resolves to a java.util.Collection (List/Set); convert arrays with Arrays.asList(...) before storing.
- Initialize the variable to an empty collection instead of leaving it null.
- If the data is a String, split it into a List in a delegate or use an expression that returns a List.
- 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
- Ensure flowable:collection expressions return List/Set, never String, array, Map, or null.
- Convert arrays with Arrays.asList and delimited strings by splitting into a List.
- Unit-test multi-instance activities with representative and empty collections.
- Prefer collectionVariable with a well-typed variable over ad-hoc expressions.
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
- completionCondition '${activeCompletionCondition}' does not
- Variable ${collectionVariable}' is not a Collection
- Variable '${obj}':${collectionVariable} is not a Collection
- Couldn't resolve collection expression${collectionExpression
- Could not resolve loopCardinality expression '${loopCardinal
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/15cb40e1b282e115.
Report an issue: GitHub.