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

  1. Store a java.util.Collection (List/Set) in the variable, e.g. Arrays.asList("a","b").
  2. Convert arrays or strings to a Collection before setting the variable.
  3. Check where the variable is overwritten downstream and fix the conflicting assignment.
  4. If it is a Map of items, store map.values() or the relevant entry instead of the Map itself.
  5. 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

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


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