flowable/flowable-engine · error · FlowableIllegalArgumentException

requested number of jobs must not be smaller than 1

Error message

requested number of jobs must not be smaller than 1

What it means

Thrown by AcquireExternalWorkerJobsCmd.execute() when numberOfJobs is less than 1. Flowable uses this value as the max result count for the acquisition query; a zero or negative value is treated as invalid input and rejected before querying.

Solutions

  1. Ensure numberOfJobs >= 1, e.g. Math.max(1, configuredBatchSize)
  2. Validate the batch-size configuration at worker startup and reject non-positive values
  3. If nothing should be acquired, skip the call entirely instead of passing 0

Example fix

// before
int numberOfJobs = config.getBatchSize(); // may be 0
builder.acquireAndLock(numberOfJobs, workerId, lockDuration);
// after
int numberOfJobs = Math.max(1, config.getBatchSize());
builder.acquireAndLock(numberOfJobs, workerId, lockDuration);
Defensive patterns

Strategy: validation

Validate before calling

if (numberOfJobs < 1) {
    throw new IllegalArgumentException("numberOfJobs must be >= 1");
}

Try / catch

try {
    acquireBuilder.topic(topic).acquireAndLock(numberOfJobs, workerId, lockDuration);
} catch (FlowableIllegalArgumentException e) {
    LOGGER.error("Acquire rejected: {}", e.getMessage());
}

Prevention

When it happens

Trigger: Passing 0 or a negative number to acquireAndLock(numberOfJobs, workerId, lockDuration); numberOfJobs computed from a batch-size config that defaulted to 0; arithmetic (e.g. remaining = total - done) yielding zero or negative.

Common situations: Batch-size property unset and parsed as 0; a poll loop computing the next batch size from quotas; copy-pasted code with placeholder 0 never replaced.

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


AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11). Data as JSON: /api/errors/0c73435633396fe3. Report an issue: GitHub.

Appendix: source

Thrown at modules/flowable-job-service/src/main/java/org/flowable/job/service/impl/cmd/AcquireExternalWorkerJobsCmd.java:61

    public AcquireExternalWorkerJobsCmd(String workerId, int numberOfJobs, ExternalWorkerJobAcquireBuilderImpl builder,
            JobServiceConfiguration jobServiceConfiguration) {
        
        this.workerId = workerId;
        this.numberOfJobs = numberOfJobs;
        this.builder = builder;
        this.jobServiceConfiguration = jobServiceConfiguration;
    }

    @Override
    public List<AcquiredExternalWorkerJob> execute(CommandContext commandContext) {
        String topic = builder.getTopic();
        if (StringUtils.isEmpty(topic)) {
            throw new FlowableIllegalArgumentException("topic must not be empty");
        }

        if (numberOfJobs < 1) {
            throw new FlowableIllegalArgumentException("requested number of jobs must not be smaller than 1");
        }

        if (StringUtils.isEmpty(workerId)) {
            throw new FlowableIllegalArgumentException("workerId must not be empty");
        }

        ExternalWorkerJobEntityManager externalWorkerJobEntityManager = jobServiceConfiguration.getExternalWorkerJobEntityManager();
        InternalJobManager internalJobManager = jobServiceConfiguration.getInternalJobManager();

        List<ExternalWorkerJobEntity> jobs = externalWorkerJobEntityManager.findExternalJobsToExecute(builder, numberOfJobs);

        int lockTimeInMillis = (int) builder.getLockDuration().abs().toMillis();
        List<AcquiredExternalWorkerJob> acquiredJobs = new ArrayList<>(jobs.size());

        for (ExternalWorkerJobEntity job : jobs) {
            lockJob(commandContext, job, lockTimeInMillis);
            Map<String, Object> variables = null;
            if (internalJobManager != null) {

View on GitHub (pinned to d6d39ce1c6)