flowable/flowable-engine · error · FlowableIllegalArgumentException
Variable '${obj}':${collectionVariable} is not a Collection
Error message
Variable '${obj}':${collectionVariable} is not a Collection What it means
Thrown by MultiInstanceActivityBehavior.resolveAndValidateCollection when the configured collection variable exists but its value is neither a Collection nor an Iterable. The message reports the variable name ('${obj}') and the offending value so you can see what type was actually stored.
Source
Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/behavior/MultiInstanceActivityBehavior.java:503
if (collectionHandler != null ) {
return createFlowableCollectionHandler(collectionHandler, execution).resolveCollection(obj, execution);
} else {
if (obj instanceof Collection) {
return (Collection) obj;
} else if (obj instanceof Iterable) {
return iterableToCollection((Iterable) obj);
} else if (obj instanceof String) {
Object collectionVariable = execution.getVariable((String) obj);
if (collectionVariable instanceof Collection) {
return (Collection) collectionVariable;
} else if (collectionVariable instanceof Iterable) {
return iterableToCollection((Iterable) collectionVariable);
} else if (collectionVariable == null) {
throw new FlowableIllegalArgumentException("Variable '" + obj + "' was not found");
} else {
throw new FlowableIllegalArgumentException("Variable '" + obj + "':" + collectionVariable + " is not a Collection");
}
} else {
throw new FlowableIllegalArgumentException(buildUnresolvedCollectionExceptionMessage());
}
}
}
protected String buildUnresolvedCollectionExceptionMessage() {
StringBuilder exceptionStringBuilder = new StringBuilder("Couldn't resolve collection expression");
if (collectionExpression != null) {
exceptionStringBuilder.append(" (");
exceptionStringBuilder.append(collectionExpression.getExpressionText());
exceptionStringBuilder.append(")");
}
exceptionStringBuilder.append(", variable reference");
if (collectionVariable != null) {
exceptionStringBuilder.append(" (");View on GitHub (pinned to d6d39ce1c6)
Solutions
- Store a java.util.Collection (List/Set) in the variable, e.g. Arrays.asList("a","b").
- Convert arrays or strings to a Collection before setting the variable.
- Check where the variable is overwritten downstream and fix the conflicting assignment.
- If it is a Map of items, store map.values() or the relevant entry instead of the Map itself.
- Log the variable type (runtimeService.getVariable) to confirm what is actually stored.
Example fix
// before
vars.put("assigneeList", "kermit,gonzo");
// after
vars.put("assigneeList", Arrays.asList("kermit", "gonzo")); Defensive patterns
Strategy: type-guard
Validate before calling
Object v = execution.getVariable("assigneeList");
if (v != null && !(v instanceof Collection) && !(v instanceof Iterable)) {
throw new IllegalStateException("assigneeList must be a Collection/Iterable, got: " + v.getClass());
} Type guard
boolean isCollection(Object v) {
return v instanceof Collection || v instanceof Iterable;
} Try / catch
try {
return task.execute(execution);
} catch (FlowableIllegalArgumentException ex) {
if (ex.getMessage().contains("is not a Collection")) {
logger.error("Wrong variable type for multi-instance collection: {}", ex.getMessage());
}
throw ex;
} Prevention
- Store List/Set (Collection or Iterable) in collection variables, never String/Map/array.
- Convert comma-separated strings via split and Arrays.asList before storing.
- Audit variable writers to ensure no downstream code overwrites with a scalar.
- Log variable types at set-time in dev environments to catch type drift early.
When it happens
Trigger: flowable:collection="assigneeList" where 'assigneeList' is present in the execution but holds e.g. a String, Map, array, single object, or Integer instead of a List/Set (or other Iterable).
Common situations: Variable set to a comma-separated String instead of a List; array (String[]) set where Flowable expects a Collection/Iterable; variable overwritten later in the process with a scalar; JSON deserialization storing a Map instead of a List; Groovy/JS expression returning a non-collection value.
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
- Couldn't resolve collection expression nor variable referenc
- Variable '${obj}' was not found
- Couldn't resolve collection expression${collectionExpression
- ${collectionExpressionText}' didn't resolve to a Collection
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/bc23af617f4b3b0a.
Report an issue: GitHub.