flowable/flowable-engine · error · ActivitiIllegalArgumentException
Invalid number of instances: must be non-negative integer…
Error message
Invalid number of instances: must be non-negative integer value, but was ${nrOfInstances} What it means
Thrown by ParallelMultiInstanceBehavior.createInstances after resolving the instance count: if nrOfInstances is negative, the engine cannot spawn a negative number of child executions, so it throws ActivitiIllegalArgumentException. Note the check is < 0; zero is accepted and the activity completes immediately.
Solutions
- Clamp the variable before the activity: execution.setVariable("nrOfItems", Math.max(0, computed))
- Fix the source expression so it cannot go negative, e.g. ${a > b ? a - b : 0}
- If a custom Collection is used, verify its size() implementation returns a correct non-negative value
Example fix
// before
execution.setVariable("nrOfItems", total - completed); // can be negative
// after
execution.setVariable("nrOfItems", Math.max(0, total - completed)); Defensive patterns
Strategy: validation
Validate before calling
int nr = ((Number) execution.getVariable("nrOfItems")).intValue();
if (nr < 0) {
execution.setVariable("nrOfItems", 0); // or reject the transition
} Type guard
public static boolean isNonNegativeInt(Object v) {
return v instanceof Number && ((Number) v).intValue() >= 0;
} Try / catch
try {
runtimeService.signal(executionId);
} catch (org.activiti.engine.ActivitiIllegalArgumentException e) {
if (e.getMessage().startsWith("Invalid number of instances")) {
// clamp the cardinality variable and retry the activity
}
} Prevention
- Clamp computed counts with Math.max(0, x) before assigning loopCardinality variables
- Avoid raw subtraction expressions inside loopCardinality expressions
- Sanitize collection sizes from external data sources before feeding multi-instance loops
- Add a start listener that validates the cardinality variable range
When it happens
Trigger: resolveNrOfInstances derives a negative count: loopCardinality expression evaluates to a negative number, or a collection variable whose Collection implementation reports a negative size (custom/broken Collection), via MultiInstanceActivityBehavior.resolveNrOfInstances.
Common situations: A variable like remaining = total - completed going below zero and being fed directly into loopCardinality; custom Collection implementations with buggy size(); arithmetic in the UEL expression itself (e.g. ${a - b}) yielding a negative result.
Understand the failure class
Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.
Related errors
- Invalid number of instances: must be a non-negative integer…
- ' ' is not valid boolean in mapException with errorCode=…
- ' didn't resolve to a Collection
- completionCondition ' ' does not evaluate to a boolean value
- completionCondition ' ' does not evaluate to a boolean value
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/c2894fdcb747c178.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable5-engine/src/main/java/org/activiti/engine/impl/bpmn/behavior/ParallelMultiInstanceBehavior.java:40
import org.flowable.engine.impl.delegate.ActivityBehavior;
/**
* @author Joram Barrez
*/
public class ParallelMultiInstanceBehavior extends MultiInstanceActivityBehavior {
public ParallelMultiInstanceBehavior(ActivityImpl activity, ActivityBehavior originalActivityBehavior) {
super(activity, originalActivityBehavior);
}
/**
* Handles the parallel case of spawning the instances. Will create child executions accordingly for every instance needed.
*/
@Override
protected void createInstances(ActivityExecution execution) {
int nrOfInstances = resolveNrOfInstances(execution);
if (nrOfInstances < 0) {
throw new ActivitiIllegalArgumentException("Invalid number of instances: must be non-negative integer value"
+ ", but was " + nrOfInstances);
}
setLoopVariable(execution, NUMBER_OF_INSTANCES, nrOfInstances);
setLoopVariable(execution, NUMBER_OF_COMPLETED_INSTANCES, 0);
setLoopVariable(execution, NUMBER_OF_ACTIVE_INSTANCES, nrOfInstances);
List<ActivityExecution> concurrentExecutions = new ArrayList<>();
for (int loopCounter = 0; loopCounter < nrOfInstances; loopCounter++) {
ActivityExecution concurrentExecution = execution.createExecution();
concurrentExecution.setActive(true);
concurrentExecution.setConcurrent(true);
concurrentExecution.setScope(false);
// In case of an embedded subprocess, and extra child execution is required
// Otherwise, all child executions would end up under the same parent,
// without any differentiation to which embedded subprocess they belong
if (isExtraScopeNeeded()) {View on GitHub (pinned to d6d39ce1c6)