Activiti/Activiti · error · ActivitiIllegalArgumentException
Invalid number of instances: must be a non-negative integer…
Error message
Invalid number of instances: must be a non-negative integer value, but was ${nrOfInstances} What it means
Thrown by SequentialMultiInstanceBehavior.createInstances when resolveNrOfInstances yields a negative count for a sequential multi-instance activity. Zero is explicitly allowed (returns immediately with no instances), but any negative value is rejected as an illegal argument.
Solutions
- Validate/clamp the count before the activity: Math.max(0, value) when setting the variable
- Fix the expression to be non-negative or guard the preceding service task that computes it
- If an empty loop should be valid, ensure the value is 0 (allowed) rather than a negative number
Example fix
// before
<completionCondition/> <!-- n/a --> <mi:loopCardinality>${count}</mi:loopCardinality> <!-- count = -2 -->
// after
// in a delegate before the MI activity
execution.setVariable("count", Math.max(0, requestedCount)); Defensive patterns
Strategy: validation
Validate before calling
Integer n = (Integer) execution.getVariable("n");
if (n == null || n < 0) throw new IllegalStateException("sequential MI instance count 'n' must be >= 0 (0 is allowed), got: " + n); Type guard
boolean isValidSequentialInstanceCount(Integer n) {
return n != null && n >= 0;
} Prevention
- Remember 0 instances is legal for sequential MI — only negatives fail
- Clamp with Math.max(0, value) where the count is computed
- Handle empty collections: size 0 is fine, negative arithmetic is not
- Validate variables in the preceding flow element before entering the MI activity
When it happens
Trigger: loopCardinality expression or numberOfInstances variable resolves negative — e.g. '${list.size() - skip}' with skip > size, or a variable initialized to -1; subclass of the parallel variant, so 0 is tolerated here but must still never be negative.
Common situations: Arithmetic on collections that may be empty or smaller than expected; countdown-style variables that start below zero; validation gap where the caller assumed Activiti clamps the value (it does not).
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Invalid number of instances: must be non-negative integer…
- Could not resolve loopCardinality expression
- completionCondition ' ' does not evaluate to a boolean value
- Couldn't resolve collection expression nor variable…
- Programmatic error: parallel gateway behaviour can only be…
AI-assisted analysis of Activiti/Activiti@56435b1a97 (2026-09-09).
Data as JSON: /api/errors/549f215611569aeb.
Report an issue: GitHub.
Appendix: source
Thrown at activiti-core/activiti-engine/src/main/java/org/activiti/engine/impl/bpmn/behavior/SequentialMultiInstanceBehavior.java:45
import org.activiti.engine.impl.persistence.entity.ExecutionEntityManager;
public class SequentialMultiInstanceBehavior extends MultiInstanceActivityBehavior {
private static final long serialVersionUID = 1L;
public SequentialMultiInstanceBehavior(Activity activity, AbstractBpmnActivityBehavior innerActivityBehavior) {
super(activity, innerActivityBehavior);
}
/**
* Handles the sequential case of spawning the instances. Will only create one instance, since at most one instance can be active.
*/
protected int createInstances(DelegateExecution multiInstanceExecution) {
int nrOfInstances = resolveNrOfInstances(multiInstanceExecution);
if (nrOfInstances == 0) {
return nrOfInstances;
} else if (nrOfInstances < 0) {
throw new ActivitiIllegalArgumentException(
"Invalid number of instances: must be a non-negative integer value" + ", but was " + nrOfInstances
);
}
// Create child execution that will execute the inner behavior
ExecutionEntity childExecution = Context.getCommandContext()
.getExecutionEntityManager()
.createChildExecution((ExecutionEntity) multiInstanceExecution);
childExecution.setCurrentFlowElement(multiInstanceExecution.getCurrentFlowElement());
multiInstanceExecution.setMultiInstanceRoot(true);
multiInstanceExecution.setActive(false);
// Set Multi-instance variables
setLoopVariable(multiInstanceExecution, NUMBER_OF_INSTANCES, nrOfInstances);
setLoopVariable(multiInstanceExecution, NUMBER_OF_COMPLETED_INSTANCES, 0);
setLoopVariable(multiInstanceExecution, NUMBER_OF_ACTIVE_INSTANCES, 1);
setLoopVariable(childExecution, getCollectionElementIndexVariable(), 0);
logLoopDetails(multiInstanceExecution, "initialized", 0, 0, 1, nrOfInstances);View on GitHub (pinned to 56435b1a97)