flowable/flowable-engine · error · FlowableIllegalArgumentException

lockDuration is required

Error message

lockDuration is required

What it means

FlowableIllegalArgumentException thrown in ExternalWorkerAcquireJobResource.acquireAndLockJobs when the acquire request supplies a topic but no lockDuration. Locking a job for a worker requires an explicit lock duration; the engine refuses to guess one. 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:74

    @ApiOperation(value = "Acquire External Worker Jobs", tags = { "Acquire and Execute" })
    @ApiResponses({
            @ApiResponse(code = 200, message = "Indicates the jobs were acquired and locked."),
            @ApiResponse(code = 400, message = "Indicates the request was invalid."),
            @ApiResponse(code = 403, message = "Indicates the user does not have the rights acquire the jobs."),
    })
    @PostMapping(value = "/acquire/jobs", produces = "application/json")
    public List<AcquiredExternalWorkerJobResponse> acquireAndLockJobs(@RequestBody AcquireExternalWorkerJobRequest request) {
        ExternalWorkerJobAcquireBuilder acquireBuilder = createExternalWorkerAcquireBuilder();

        if (restApiInterceptor != null) {
            restApiInterceptor.accessAcquireExternalWorkerJobs(acquireBuilder, request);
        }

        if (StringUtils.isNotEmpty(request.getTopic())) {
            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");
        }
    }

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Add lockDuration (milliseconds) to the acquire request body.
  2. Set a default lockDuration in the client request builder.
  3. Fix JSON field spelling so the value actually binds to getLockDuration().

Example fix

// before
POST /external-worker/acquire/jobs
{"topic": "invoice-processing", "workerId": "w1"}
// after
POST /external-worker/acquire/jobs
{"topic": "invoice-processing", "workerId": "w1", "lockDuration": 60000}
Defensive patterns

Strategy: validation

Validate before calling

if (request.topic() != null && (request.lockDuration() == null || request.lockDuration() <= 0)) {
    throw new IllegalArgumentException("lockDuration is required when topic is set");
}

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 a request body containing {"topic":"myTopic"} but omitting lockDuration (null).

Common situations: Client DTO lacking a default lockDuration; JSON field named incorrectly (e.g. lockTime) so it deserializes to null; copying an example request that sets topic only.

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/6b0ae21e78798d2b. Report an issue: GitHub.