flowable/flowable-engine · error · FlowableIllegalArgumentException

workerId is required

Error message

workerId is required

What it means

FlowableIllegalArgumentException thrown in acquireAndLockJobs when workerId is empty or null. The engine records the workerId as the lock owner so other workers cannot complete the job; acquiring without it is rejected. Results in HTTP 400.

Source

Thrown at modules/flowable-external-job-rest/src/main/java/org/flowable/external/job/rest/service/api/acquire/ExternalWorkerAcquireJobResource.java:89

            if (request.getLockDuration() != null) {
                acquireBuilder.topic(request.getTopic(), request.getLockDuration());
            } else {
                throw new FlowableIllegalArgumentException("lockDuration is required");
            }
        } else {
            throw new FlowableIllegalArgumentException("topic is required");
        }

        if (request.getScopeType() != null) {
            acquireBuilder.scopeType(request.getScopeType());
        }

        if (StringUtils.isNotEmpty(request.getWorkerId())) {
            List<AcquiredExternalWorkerJob> acquiredJobs = acquireBuilder
                    .acquireAndLock(request.getNumberOfTasks(), request.getWorkerId(), request.getNumberOfRetries());
            return restResponseFactory.createAcquiredExternalWorkerJobResponseList(acquiredJobs);
        } else {
            throw new FlowableIllegalArgumentException("workerId is required");
        }
    }

    @ApiOperation(value = "Complete an External Worker Jobs", code = 204, tags = { "Acquire and Execute" })
    @ApiResponses({
            @ApiResponse(code = 204, message = "Indicates the job was successfully completed."),
            @ApiResponse(code = 400, message = "Indicates the request was invalid."),
            @ApiResponse(code = 403, message = "Indicates the user does not have the rights complete the job."),
            @ApiResponse(code = 404, message = "Indicates the job does not exist."),
    })
    @PostMapping(value = "/acquire/jobs/{jobId}/complete", produces = "application/json")
    public ResponseEntity<?> completeJob(@PathVariable String jobId, @RequestBody ExternalWorkerJobCompleteRequest request) {
        String workerId = request.getWorkerId();
        if (StringUtils.isEmpty(workerId)) {
            throw new FlowableIllegalArgumentException("workerId is required");
        }

        ExternalWorkerJob job = getExternalWorkerJobById(jobId);

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Set a unique workerId in the acquire request (e.g. hostname + UUID).
  2. Initialize the worker identity before starting the acquire loop.
  3. Fix JSON binding so workerId is not null.

Example fix

// before
{"topic": "invoices", "lockDuration": 60000}
// after
{"topic": "invoices", "lockDuration": 60000, "workerId": "worker-1"}
Defensive patterns

Strategy: validation

Validate before calling

if (request.workerId() == null || request.workerId().isBlank()) {
    throw new IllegalArgumentException("workerId is required");
}

Try / catch

try {
    acquire(request);
} catch (HttpClientErrorException.BadRequest e) {
    log.error("Acquire rejected: {}", e.getResponseBodyAsString());
}

Prevention

When it happens

Trigger: POST /external-worker/acquire/jobs with missing or empty workerId field in the request body.

Common situations: Worker instance hostname/UUID not yet initialized when the first acquire fires; config key for worker id missing; field name mismatch during JSON binding.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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