{"record":{"id":"c780018f57377603","repo":"flowable/flowable-engine","slug":"variable-collectionvariable-is-not-a-collectio","errorCode":null,"errorMessage":"Variable ${collectionVariable}' is not a Collection","messagePattern":"Variable (.+?)' is not a Collection","errorType":"validation","errorClass":"ActivitiIllegalArgumentException","httpStatus":null,"severity":"error","filePath":"modules/flowable5-engine/src/main/java/org/activiti/engine/impl/bpmn/behavior/MultiInstanceActivityBehavior.java","lineNumber":152,"sourceCode":"\n    @SuppressWarnings(\"rawtypes\")\n    protected int resolveNrOfInstances(ActivityExecution execution) {\n        int nrOfInstances = -1;\n        if (loopCardinalityExpression != null) {\n            nrOfInstances = resolveLoopCardinality(execution);\n        } else if (collectionExpression != null) {\n            Object obj = collectionExpression.getValue(execution);\n            if (!(obj instanceof Collection)) {\n                throw new ActivitiIllegalArgumentException(collectionExpression.getExpressionText() + \"' didn't resolve to a Collection\");\n            }\n            nrOfInstances = ((Collection) obj).size();\n        } else if (collectionVariable != null) {\n            Object obj = execution.getVariable(collectionVariable);\n            if (obj == null) {\n                throw new ActivitiIllegalArgumentException(\"Variable \" + collectionVariable + \" was not found\");\n            }\n            if (!(obj instanceof Collection)) {\n                throw new ActivitiIllegalArgumentException(\"Variable \" + collectionVariable + \"' is not a Collection\");\n            }\n            nrOfInstances = ((Collection) obj).size();\n        } else {\n            throw new ActivitiIllegalArgumentException(\"Couldn't resolve collection expression nor variable reference\");\n        }\n        return nrOfInstances;\n    }\n\n    @SuppressWarnings(\"rawtypes\")\n    protected void executeOriginalBehavior(ActivityExecution execution, int loopCounter) {\n        if (usesCollection() && collectionElementVariable != null) {\n            Collection collection = null;\n            if (collectionExpression != null) {\n                collection = (Collection) collectionExpression.getValue(execution);\n            } else if (collectionVariable != null) {\n                collection = (Collection) execution.getVariable(collectionVariable);\n            }\n","sourceCodeStart":134,"sourceCodeEnd":170,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable5-engine/src/main/java/org/activiti/engine/impl/bpmn/behavior/MultiInstanceActivityBehavior.java#L134-L170","documentation":"When a multi-instance activity references a collectionVariable, the variable must be a java.util.Collection. If the variable exists but holds a non-Collection value (String, array, Map, custom object), resolveNrOfInstances throws this ActivitiIllegalArgumentException. Note the message has a stray quote before 'is'.","triggerScenarios":"flowable:collection=\"myVar\" where execution.getVariable(\"myVar\") returns a non-null, non-Collection object when the multi-instance activity is entered.","commonSituations":"Storing a comma-separated String where a List is expected; serializing JSON into a String variable; putting an array (Object[]) into the variable; a Map is used (Map is not a Collection); a REST/HTTP response body stored as String; type changed between process versions.","solutions":["Store a java.util.Collection (e.g. ArrayList) in the variable instead of a String/array/map.","Convert in a delegate or listener: Arrays.asList(array) or split a delimited string into a List.","If it must stay a Map, iterate its keySet()/values() and store that Collection in a separate variable.","Audit other writers of the variable (listeners, scripts, REST API set-variable calls) for wrong types."],"exampleFix":"// before\nMap<String, String> userMap = fetchUsers();\nexecution.setVariable(\"users\", userMap);\n\n// after\nList<String> users = new ArrayList<>(fetchUsers().keySet());\nexecution.setVariable(\"users\", users);","handlingStrategy":"type-guard","validationCode":"// Validate the variable type before the multi-instance activity runs\nObject v = execution.getVariable(\"users\");\nif (v != null && !(v instanceof java.util.Collection)) {\n    throw new IllegalStateException(\"Variable 'users' must be a java.util.Collection, got \" + v.getClass().getName());\n}","typeGuard":"java.util.Collection<?> asCollection(Object o) {\n    if (o instanceof java.util.Collection) return (java.util.Collection<?>) o;\n    if (o instanceof Object[]) return java.util.Arrays.asList((Object[]) o);\n    if (o instanceof String) return java.util.Arrays.asList(((String) o).split(\",\"));\n    if (o instanceof java.util.Map) return ((java.util.Map<?, ?>) o).keySet();\n    return null;\n}","tryCatchPattern":"try {\n    runtimeService.startProcessInstanceByKey(key, vars);\n} catch (org.activiti.engine.ActivitiIllegalArgumentException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"' is not a Collection\")) {\n        // convert the variable to a java.util.Collection and retry the process instance\n    } else {\n        throw e;\n    }\n}","preventionTips":["Only store java.util.Collection instances in variables referenced by flowable:collection.","Remember Map, Object[] and String are NOT Collections even though they feel collection-like.","Centralize variable creation in one delegate to keep types consistent.","Check all writers (scripts, REST API, listeners) of the variable enforce the collection type."],"tags":["bpmn","process-engine","multi-instance","variables","type-mismatch"],"backgroundTag":"type-mismatch","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}