{"record":{"id":"15cb40e1b282e115","repo":"flowable/flowable-engine","slug":"collectionexpressiontext-didn-t-resolve-to-a-c","errorCode":null,"errorMessage":"${collectionExpressionText}' didn't resolve to a Collection","messagePattern":"(.+?)' didn't resolve to 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":143,"sourceCode":"    }\n\n    // required for supporting external subprocesses\n    @Override\n    public void completed(ActivityExecution execution) throws Exception {\n        leave(execution);\n    }\n\n    // Helpers //////////////////////////////////////////////////////////////////////\n\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\")","sourceCodeStart":125,"sourceCodeEnd":161,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable5-engine/src/main/java/org/activiti/engine/impl/bpmn/behavior/MultiInstanceActivityBehavior.java#L125-L161","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before\nString assignees = \"john,jane\";\nexecution.setVariable(\"assignees\", assignees);\n\n// after\nList<String> assignees = Arrays.asList(\"john\", \"jane\");\nexecution.setVariable(\"assignees\", assignees);","handlingStrategy":"validation","validationCode":"// Validate before the process reaches the multi-instance activity\nObject value = expression != null ? expression.getValue(execution) : null;\nif (!(value instanceof java.util.Collection)) {\n    throw new IllegalArgumentException(\"flowable:collection expression must resolve to a java.util.Collection, got: \"\n        + (value == null ? \"null\" : value.getClass().getName()));\n}","typeGuard":"boolean isCollectionForMultiInstance(Object o) {\n    return o instanceof java.util.Collection && !((java.util.Collection<?>) o).isEmpty() || o instanceof java.util.Collection;\n}","tryCatchPattern":"try {\n    runtimeService.startProcessInstanceByKey(key, vars);\n} catch (org.activiti.engine.ActivitiIllegalArgumentException e) {\n    if (e.getMessage() != null && e.getMessage().endsWith(\"' didn't resolve to a Collection\")) {\n        // fix the expression/variable to return a java.util.Collection\n    } else {\n        throw e;\n    }\n}","preventionTips":["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."],"tags":["bpmn","process-engine","multi-instance","expression","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"}