{"record":{"id":"2d53b2ef43d234bd","repo":"flowable/flowable-engine","slug":"does-not-hold-a-lock-on-the-requested-job","errorCode":null,"errorMessage":" does not hold a lock on the requested job","messagePattern":" does not hold a lock on the requested job","errorType":"validation","errorClass":"FlowableIllegalArgumentException","httpStatus":null,"severity":"error","filePath":"modules/flowable-job-service/src/main/java/org/flowable/job/service/impl/cmd/AbstractExternalWorkerJobCmd.java","lineNumber":74,"sourceCode":"    protected ExternalWorkerJobEntity resolveJob() {\n        if (StringUtils.isEmpty(externalJobId)) {\n            throw new FlowableIllegalArgumentException(\"externalJobId must not be empty\");\n        }\n\n        if (StringUtils.isEmpty(workerId)) {\n            throw new FlowableIllegalArgumentException(\"workerId must not be empty\");\n        }\n\n        ExternalWorkerJobEntityManager externalWorkerJobEntityManager = jobServiceConfiguration.getExternalWorkerJobEntityManager();\n\n        ExternalWorkerJobEntity job = externalWorkerJobEntityManager.findById(externalJobId);\n\n        if (job == null) {\n            throw new FlowableObjectNotFoundException(\"No External Worker job found for id: \" + externalJobId, ExternalWorkerJobEntity.class);\n        }\n\n        if (!Objects.equals(workerId, job.getLockOwner())) {\n            throw new FlowableIllegalArgumentException(workerId + \" does not hold a lock on the requested job\");\n        }\n\n        return job;\n    }\n}\n","sourceCodeStart":56,"sourceCodeEnd":80,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-job-service/src/main/java/org/flowable/job/service/impl/cmd/AbstractExternalWorkerJobCmd.java#L56-L80","documentation":"Thrown by resolveJob() as FlowableIllegalArgumentException when the given workerId does not equal the job's lockOwner. Flowable pessimistically locks acquired jobs to a specific worker; only the lock owner may complete, fail, or terminate it. This guards against two workers mutating the same job concurrently.","triggerScenarios":"Worker A tries to complete a job that worker B currently has locked; workerId changed (restart with new generated id) while old lock persists; executing a completion command on a job whose lock expired and was re-acquired by another worker; hardcoding or misconfiguring workerId so it differs from the acquisition one.","commonSituations":"Multiple replicas of a worker sharing one workerId configuration that is actually instance-specific; auto-scaling generating new worker ids while old handlers still run; job timeout re-assignment between acquire and complete.","solutions":["Use the exact same workerId for acquire and for complete/fail/terminate calls","Catch FlowableIllegalArgumentException and re-acquire the job instead of forcing completion","Check lock duration: if processing exceeds the lock time, extend the lock or increase the acquire lock duration","Ensure worker ids are stable per instance or per logical consumer group consistently with your locking strategy"],"exampleFix":"// before\n// acquiring with workerId \"worker-42\", completing later with whatever id\nmanagementService.createExternalWorkerJobCompletionBuilder(jobId).workerId(currentName).complete();\n// after\nmanagementService.createExternalWorkerJobCompletionBuilder(jobId).workerId(ACQUIRED_WORKER_ID).complete();","handlingStrategy":"try-catch","validationCode":"boolean ownsJob = workerId != null && workerId.equals(job.getLockOwner());","typeGuard":null,"tryCatchPattern":"try {\n    completionBuilder.externalJobId(jobId).workerId(workerId).complete();\n} catch (FlowableIllegalArgumentException e) {\n    LOGGER.warn(\"Lock lost on job {}: {}\", jobId, e.getMessage());\n    // re-acquire or give the job back\n}","preventionTips":["Use one identical workerId for acquire and complete","Set lock durations comfortably above worst-case processing time","Handle lock loss by re-acquiring rather than retrying completion with the same stale lock"],"tags":["concurrency","locking","flowable"],"backgroundTag":"invalid-argument-value","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-18T11:17:12.947Z"}