{"record":{"id":"ef8a7053b2e6011d","repo":"flowable/flowable-engine","slug":"jobentity-is-locked-with-a-different-worker-id","errorCode":null,"errorMessage":"${jobEntity} is locked with a different worker id","messagePattern":"(.+?) is locked with a different worker id","errorType":"exception","errorClass":"FlowableException","httpStatus":null,"severity":"error","filePath":"modules/flowable-job-service/src/main/java/org/flowable/job/service/impl/cmd/UnacquireExternalWorkerJobCmd.java","lineNumber":54,"sourceCode":"    @Override\n    public Void execute(CommandContext commandContext) {\n        if (StringUtils.isEmpty(jobId)) {\n            throw new FlowableIllegalArgumentException(\"job id must not be empty\");\n        }\n\n        if (StringUtils.isEmpty(workerId)) {\n            throw new FlowableIllegalArgumentException(\"worker id must not be empty\");\n        }\n\n        ExternalWorkerJobEntityManager externalWorkerJobEntityManager = jobServiceConfiguration.getExternalWorkerJobEntityManager();\n\n        ExternalWorkerJobEntity jobEntity = externalWorkerJobEntityManager.findById(jobId);\n        if (jobEntity == null) {\n            throw new FlowableException(\"Could not find job for id \" + jobId);\n        }\n        \n        if (!jobEntity.getLockOwner().equals(workerId)) {\n            throw new FlowableException(jobEntity + \" is locked with a different worker id\");\n        }\n\n        jobEntity.setLockExpirationTime(null);\n        jobEntity.setLockOwner(null);\n        externalWorkerJobEntityManager.update(jobEntity);\n        if (jobEntity.isExclusive()) {\n            new UnlockExclusiveJobCmd(jobEntity, jobServiceConfiguration).execute(commandContext);\n        }\n        \n        return null;\n    }\n}\n","sourceCodeStart":36,"sourceCodeEnd":67,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-job-service/src/main/java/org/flowable/job/service/impl/cmd/UnacquireExternalWorkerJobCmd.java#L36-L67","documentation":"UnacquireExternalWorkerJobCmd.execute throws this FlowableException when the lock owner (LOCK_OWNER_ column) of the external worker job does not equal the workerId passed to the command. The message stringifies the job entity. This prevents one worker from releasing a job that another worker currently owns.","triggerScenarios":"Calling unacquireExternalWorkerJob(jobId, otherWorkerId) where the job was acquired by a different worker id; reusing the same worker id configured differently across instances; the job was re-acquired by another worker after lock expiry while the first worker still tries to release it.","commonSituations":"Multiple worker instances sharing a database but configured with the same/default worker id then one restarts with a new id; load-balanced handlers releasing each other's jobs; lock expiry races under long-running task handling.","solutions":["Pass the exact worker id that acquired the job (JobEntity.getLockOwner()) to unacquire","Give each worker instance a unique, persistent worker id in configuration","Catch FlowableException and skip release if another worker owns the lock","Keep task processing within the lock duration to avoid re-acquisition races"],"exampleFix":"// before\njobService.unacquireExternalWorkerJob(job.getId(), \"worker-2\"); // job locked by worker-1\n// after\njobService.unacquireExternalWorkerJob(job.getId(), job.getLockOwner());","handlingStrategy":"try-catch","validationCode":"ExternalWorkerJobEntity job = externalWorkerJobService.createExternalWorkerJobQuery().jobId(jobId).singleResult();\nif (job != null && workerId.equals(job.getLockOwner())) {\n    externalWorkerJobService.unacquireExternalWorkerJob(jobId, workerId);\n}","typeGuard":null,"tryCatchPattern":"try {\n    externalWorkerJobService.unacquireExternalWorkerJob(jobId, workerId);\n} catch (FlowableException e) {\n    logger.info(\"Job {} locked by another worker; skipping unacquire\", jobId);\n}","preventionTips":["Unacquire with the same worker id that acquired the job","Give each worker instance a unique, stable worker id","Handle lock-expiry races by re-checking ownership before release"],"tags":["locking","flowable","external-worker","ownership"],"backgroundTag":"invalid-state-transition","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"}