{"record":{"id":"549f215611569aeb","repo":"Activiti/Activiti","slug":"invalid-number-of-instances-must-be-a-non-negativ","errorCode":null,"errorMessage":"Invalid number of instances: must be a non-negative integer value, but was ${nrOfInstances}","messagePattern":"Invalid number of instances: must be a non-negative integer value, but was (.+?)","errorType":"validation","errorClass":"ActivitiIllegalArgumentException","httpStatus":null,"severity":"error","filePath":"activiti-core/activiti-engine/src/main/java/org/activiti/engine/impl/bpmn/behavior/SequentialMultiInstanceBehavior.java","lineNumber":45,"sourceCode":"import org.activiti.engine.impl.persistence.entity.ExecutionEntityManager;\n\npublic class SequentialMultiInstanceBehavior extends MultiInstanceActivityBehavior {\n\n    private static final long serialVersionUID = 1L;\n\n    public SequentialMultiInstanceBehavior(Activity activity, AbstractBpmnActivityBehavior innerActivityBehavior) {\n        super(activity, innerActivityBehavior);\n    }\n\n    /**\n     * Handles the sequential case of spawning the instances. Will only create one instance, since at most one instance can be active.\n     */\n    protected int createInstances(DelegateExecution multiInstanceExecution) {\n        int nrOfInstances = resolveNrOfInstances(multiInstanceExecution);\n        if (nrOfInstances == 0) {\n            return nrOfInstances;\n        } else if (nrOfInstances < 0) {\n            throw new ActivitiIllegalArgumentException(\n                \"Invalid number of instances: must be a non-negative integer value\" + \", but was \" + nrOfInstances\n            );\n        }\n\n        // Create child execution that will execute the inner behavior\n        ExecutionEntity childExecution = Context.getCommandContext()\n            .getExecutionEntityManager()\n            .createChildExecution((ExecutionEntity) multiInstanceExecution);\n        childExecution.setCurrentFlowElement(multiInstanceExecution.getCurrentFlowElement());\n        multiInstanceExecution.setMultiInstanceRoot(true);\n        multiInstanceExecution.setActive(false);\n\n        // Set Multi-instance variables\n        setLoopVariable(multiInstanceExecution, NUMBER_OF_INSTANCES, nrOfInstances);\n        setLoopVariable(multiInstanceExecution, NUMBER_OF_COMPLETED_INSTANCES, 0);\n        setLoopVariable(multiInstanceExecution, NUMBER_OF_ACTIVE_INSTANCES, 1);\n        setLoopVariable(childExecution, getCollectionElementIndexVariable(), 0);\n        logLoopDetails(multiInstanceExecution, \"initialized\", 0, 0, 1, nrOfInstances);","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/Activiti/Activiti/blob/56435b1a97deeafdc09dd40074b056c89fba5a8a/activiti-core/activiti-engine/src/main/java/org/activiti/engine/impl/bpmn/behavior/SequentialMultiInstanceBehavior.java#L27-L63","documentation":"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.","triggerScenarios":"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.","commonSituations":"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).","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"],"exampleFix":"// before\n<completionCondition/> <!-- n/a --> <mi:loopCardinality>${count}</mi:loopCardinality> <!-- count = -2 -->\n\n// after\n// in a delegate before the MI activity\nexecution.setVariable(\"count\", Math.max(0, requestedCount));","handlingStrategy":"validation","validationCode":"Integer n = (Integer) execution.getVariable(\"n\");\nif (n == null || n < 0) throw new IllegalStateException(\"sequential MI instance count 'n' must be >= 0 (0 is allowed), got: \" + n);","typeGuard":"boolean isValidSequentialInstanceCount(Integer n) {\n    return n != null && n >= 0;\n}","tryCatchPattern":null,"preventionTips":["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"],"tags":["bpmn","workflow","invalid-argument-value","negative-number"],"backgroundTag":"invalid-argument-value","analyzedSha":"56435b1a97deeafdc09dd40074b056c89fba5a8a","analyzedAt":"2026-09-09T21:00:06.703Z","contentChangedAt":"2026-09-09T21:00:06.703Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}