flowable/flowable-engine · error · ActivitiIllegalArgumentException
Could not resolve loopCardinality expression
Error message
Could not resolve loopCardinality expression '${expressionText}': not a number nor number String What it means
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.
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
Example fix
// before
execution.setVariable("nrOfItems", items.size() > 0); // Boolean
// after
execution.setVariable("nrOfItems", items.size()); // Integer Defensive patterns
Strategy: type-guard
Validate before calling
Object v = execution.getVariable("nrOfItems");
if (v == null || (!(v instanceof Number) && !(v instanceof String && ((String) v).matches("\\d+")))) {
throw new IllegalStateException("nrOfItems must be a number or numeric String");
} Type guard
public static boolean isCardinalityValue(Object v) {
return v instanceof Number || (v instanceof String && ((String) v).trim().matches("\\d+"));
} Try / catch
try {
runtimeService.signal(executionId);
} catch (org.activiti.engine.ActivitiIllegalArgumentException e) {
if (e.getMessage().contains("not a number nor number String")) {
// coerce the variable to Integer and restart/retry the activity
}
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
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
- ' didn't resolve to a Collection
- completionCondition ' ' does not evaluate to a boolean value
- completionCondition ' ' does not evaluate to a boolean value
- Variable ' is not a Collection
- Variable ' ': is not a Collection
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/415bd8433438cee7.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable5-engine/src/main/java/org/activiti/engine/impl/bpmn/behavior/MultiInstanceActivityBehavior.java:209
protected boolean usesCollection() {
return collectionExpression != null
|| collectionVariable != null;
}
protected boolean isExtraScopeNeeded() {
// special care is needed when the behavior is an embedded subprocess (not very clean, but it works)
return innerActivityBehavior instanceof org.activiti.engine.impl.bpmn.behavior.SubProcessActivityBehavior;
}
protected int resolveLoopCardinality(ActivityExecution execution) {
// Using Number since expr can evaluate to eg. Long (which is also the default for Juel)
Object value = loopCardinalityExpression.getValue(execution);
if (value instanceof Number) {
return ((Number) value).intValue();
} else if (value instanceof String) {
return Integer.valueOf((String) value);
} else {
throw new ActivitiIllegalArgumentException("Could not resolve loopCardinality expression '"
+ loopCardinalityExpression.getExpressionText() + "': not a number nor number String");
}
}
protected boolean completionConditionSatisfied(ActivityExecution execution) {
if (completionConditionExpression != null) {
Object value = completionConditionExpression.getValue(execution);
if (!(value instanceof Boolean)) {
throw new ActivitiIllegalArgumentException("completionCondition '"
+ completionConditionExpression.getExpressionText()
+ "' does not evaluate to a boolean value");
}
Boolean booleanValue = (Boolean) value;
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Completion condition of multi-instance satisfied: {}", booleanValue);
}
return booleanValue;
}View on GitHub (pinned to d6d39ce1c6)