{"record":{"id":"415bd8433438cee7","repo":"flowable/flowable-engine","slug":"could-not-resolve-loopcardinality-expression-ex","errorCode":null,"errorMessage":"Could not resolve loopCardinality expression '${expressionText}': not a number nor number String","messagePattern":"Could not resolve loopCardinality expression '(.+?)': not a number nor number String","errorType":"validation","errorClass":"ActivitiIllegalArgumentException","httpStatus":null,"severity":"error","filePath":"modules/flowable5-engine/src/main/java/org/activiti/engine/impl/bpmn/behavior/MultiInstanceActivityBehavior.java","lineNumber":209,"sourceCode":"    protected boolean usesCollection() {\n        return collectionExpression != null\n                || collectionVariable != null;\n    }\n\n    protected boolean isExtraScopeNeeded() {\n        // special care is needed when the behavior is an embedded subprocess (not very clean, but it works)\n        return innerActivityBehavior instanceof org.activiti.engine.impl.bpmn.behavior.SubProcessActivityBehavior;\n    }\n\n    protected int resolveLoopCardinality(ActivityExecution execution) {\n        // Using Number since expr can evaluate to eg. Long (which is also the default for Juel)\n        Object value = loopCardinalityExpression.getValue(execution);\n        if (value instanceof Number) {\n            return ((Number) value).intValue();\n        } else if (value instanceof String) {\n            return Integer.valueOf((String) value);\n        } else {\n            throw new ActivitiIllegalArgumentException(\"Could not resolve loopCardinality expression '\"\n                    + loopCardinalityExpression.getExpressionText() + \"': not a number nor number String\");\n        }\n    }\n\n    protected boolean completionConditionSatisfied(ActivityExecution execution) {\n        if (completionConditionExpression != null) {\n            Object value = completionConditionExpression.getValue(execution);\n            if (!(value instanceof Boolean)) {\n                throw new ActivitiIllegalArgumentException(\"completionCondition '\"\n                        + completionConditionExpression.getExpressionText()\n                        + \"' does not evaluate to a boolean value\");\n            }\n            Boolean booleanValue = (Boolean) value;\n            if (LOGGER.isDebugEnabled()) {\n                LOGGER.debug(\"Completion condition of multi-instance satisfied: {}\", booleanValue);\n            }\n            return booleanValue;\n        }","sourceCodeStart":191,"sourceCodeEnd":227,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable5-engine/src/main/java/org/activiti/engine/impl/bpmn/behavior/MultiInstanceActivityBehavior.java#L191-L227","documentation":"Thrown by MultiInstanceActivityBehavior.resolveLoopCardinality when the loopCardinality expression evaluates to an object that is neither a java.lang.Number nor a String parseable as an integer. The engine requires an int to know how many instances to spawn, so any other type is rejected.","triggerScenarios":"activiti:loopCardinality=\"${someVar}\" where someVar is a Boolean, Date, Map, or arbitrary bean; the expression itself is fine syntactically but returns a non-numeric, non-String value.","commonSituations":"Pointing loopCardinality at a String with whitespace or non-numeric characters (Integer.valueOf throws NumberFormatException, wrapped or surfaced here for non-String types); a UEL expression returning e.g. a Long wrapper is fine, but a custom type or null result is not; copy-pasting a completion condition (boolean) into loopCardinality.","solutions":["Make the loopCardinality expression evaluate to a numeric type or numeric String, e.g. activiti:loopCardinality=\"${nrOfItems}\" with nrOfInstances as Integer","Convert in a listener/delegate: execution.setVariable(\"nrOfItems\", Integer.parseInt(raw)) before the activity starts","If the value is a countable object, switch to activiti:collection and let the engine derive the count from its size"],"exampleFix":"// before\nexecution.setVariable(\"nrOfItems\", items.size() > 0); // Boolean\n// after\nexecution.setVariable(\"nrOfItems\", items.size()); // Integer","handlingStrategy":"type-guard","validationCode":"Object v = execution.getVariable(\"nrOfItems\");\nif (v == null || (!(v instanceof Number) && !(v instanceof String && ((String) v).matches(\"\\\\d+\")))) {\n    throw new IllegalStateException(\"nrOfItems must be a number or numeric String\");\n}","typeGuard":"public static boolean isCardinalityValue(Object v) {\n    return v instanceof Number || (v instanceof String && ((String) v).trim().matches(\"\\\\d+\"));\n}","tryCatchPattern":"try {\n    runtimeService.signal(executionId);\n} catch (org.activiti.engine.ActivitiIllegalArgumentException e) {\n    if (e.getMessage().contains(\"not a number nor number String\")) {\n        // coerce the variable to Integer and restart/retry the activity\n    }\n}","preventionTips":["Store loop cardinality sources as Integer/Long variables, not Strings or domain objects","Never wire boolean or date variables into loopCardinality","Add a validation listener before the multi-instance activity","Unit-test expressions with the exact runtime variable types"],"tags":["bpmn","multi-instance","expression-evaluation","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-18T11:17:12.947Z"}