{"record":{"id":"6b0ae21e78798d2b","repo":"flowable/flowable-engine","slug":"lockduration-is-required","errorCode":null,"errorMessage":"lockDuration is required","messagePattern":"lockDuration is required","errorType":"validation","errorClass":"FlowableIllegalArgumentException","httpStatus":400,"severity":"error","filePath":"modules/flowable-external-job-rest/src/main/java/org/flowable/external/job/rest/service/api/acquire/ExternalWorkerAcquireJobResource.java","lineNumber":74,"sourceCode":"    @ApiOperation(value = \"Acquire External Worker Jobs\", tags = { \"Acquire and Execute\" })\n    @ApiResponses({\n            @ApiResponse(code = 200, message = \"Indicates the jobs were acquired and locked.\"),\n            @ApiResponse(code = 400, message = \"Indicates the request was invalid.\"),\n            @ApiResponse(code = 403, message = \"Indicates the user does not have the rights acquire the jobs.\"),\n    })\n    @PostMapping(value = \"/acquire/jobs\", produces = \"application/json\")\n    public List<AcquiredExternalWorkerJobResponse> acquireAndLockJobs(@RequestBody AcquireExternalWorkerJobRequest request) {\n        ExternalWorkerJobAcquireBuilder acquireBuilder = createExternalWorkerAcquireBuilder();\n\n        if (restApiInterceptor != null) {\n            restApiInterceptor.accessAcquireExternalWorkerJobs(acquireBuilder, request);\n        }\n\n        if (StringUtils.isNotEmpty(request.getTopic())) {\n            if (request.getLockDuration() != null) {\n                acquireBuilder.topic(request.getTopic(), request.getLockDuration());\n            } else {\n                throw new FlowableIllegalArgumentException(\"lockDuration is required\");\n            }\n        } else {\n            throw new FlowableIllegalArgumentException(\"topic is required\");\n        }\n\n        if (request.getScopeType() != null) {\n            acquireBuilder.scopeType(request.getScopeType());\n        }\n\n        if (StringUtils.isNotEmpty(request.getWorkerId())) {\n            List<AcquiredExternalWorkerJob> acquiredJobs = acquireBuilder\n                    .acquireAndLock(request.getNumberOfTasks(), request.getWorkerId(), request.getNumberOfRetries());\n            return restResponseFactory.createAcquiredExternalWorkerJobResponseList(acquiredJobs);\n        } else {\n            throw new FlowableIllegalArgumentException(\"workerId is required\");\n        }\n    }\n","sourceCodeStart":56,"sourceCodeEnd":92,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-external-job-rest/src/main/java/org/flowable/external/job/rest/service/api/acquire/ExternalWorkerAcquireJobResource.java#L56-L92","documentation":"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.","triggerScenarios":"POST /external-worker/acquire/jobs with a request body containing {\"topic\":\"myTopic\"} but omitting lockDuration (null).","commonSituations":"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.","solutions":["Add lockDuration (milliseconds) to the acquire request body.","Set a default lockDuration in the client request builder.","Fix JSON field spelling so the value actually binds to getLockDuration()."],"exampleFix":"// before\nPOST /external-worker/acquire/jobs\n{\"topic\": \"invoice-processing\", \"workerId\": \"w1\"}\n// after\nPOST /external-worker/acquire/jobs\n{\"topic\": \"invoice-processing\", \"workerId\": \"w1\", \"lockDuration\": 60000}","handlingStrategy":"validation","validationCode":"if (request.topic() != null && (request.lockDuration() == null || request.lockDuration() <= 0)) {\n    throw new IllegalArgumentException(\"lockDuration is required when topic is set\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    acquire(request);\n} catch (HttpClientErrorException.BadRequest e) {\n    log.error(\"Acquire rejected: {}\", e.getResponseBodyAsString());\n}","preventionTips":["Build acquire requests via a typed builder that requires lockDuration.","Use explicit field names matching the API contract in JSON.","Set a sensible default lockDuration (e.g. 60s) in client config."],"tags":["rest","validation","external-worker","lock-duration"],"backgroundTag":"missing-required-argument","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}