{"record":{"id":"af6160f3dd7f58cd","repo":"flowable/flowable-engine","slug":"workerid-does-not-hold-a-lock-on-the-requested","errorCode":null,"errorMessage":"${workerId} 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-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/cmd/AbstractExternalWorkerJobCmd.java","lineNumber":91,"sourceCode":"        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        CmmnEngineConfiguration cmmnEngineConfiguration = CommandContextUtil.getCmmnEngineConfiguration(commandContext);\n        ExternalWorkerJobEntityManager externalWorkerJobEntityManager = cmmnEngineConfiguration.getJobServiceConfiguration().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":73,"sourceCodeEnd":97,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/cmd/AbstractExternalWorkerJobCmd.java#L73-L97","documentation":"This FlowableIllegalArgumentException is thrown by AbstractExternalWorkerJobCmd.resolveJob when the job exists but its lock owner does not equal the workerId supplied to the command. The engine uses lockOwner to track which external worker acquired the job, so only the acquiring worker id may complete, terminate or otherwise act on the job. It protects against two workers mutating the same job.","triggerScenarios":"Calling complete/terminate/bpmn-error style external worker commands with a workerId different from the one used during acquire; worker restarted or reconfigured with a new worker id while holding ids of jobs locked under the old id; hardcoded/mismatched workerId in test code.","commonSituations":"Load-balanced worker fleet where each instance uses a unique workerId but shares acquired job ids via a queue; config change (e.g. workerId derived from hostname) after failover; copy-pasted test code using wrong workerId constant.","solutions":["Use the same workerId value that was passed to acquireExternalWorkerJobs when acting on the job","Check job.getLockOwner() before executing the command and skip/log when it does not match your workerId","If the job is locked by a dead worker, wait for lock expiration or have an admin release the lock, then re-acquire","Standardize workerId configuration across all worker instances handling a given job queue"],"exampleFix":"// before\nmanagementService.executeCommand(new ExternalWorkerJobCompleteCmd<>(jobId, \"worker-b\", variables));\n// after\nExternalWorkerJob job = cmmnTaskService.createExternalWorkerJobQueryBuilder().externalWorkerJobId(jobId).singleResult();\nif (job != null && \"worker-b\".equals(job.getLockOwner())) {\n    managementService.executeCommand(new ExternalWorkerJobCompleteCmd<>(jobId, \"worker-b\", variables));\n} else {\n    logger.info(\"Job {} not owned by this worker (lockOwner={})\", jobId, job == null ? null : job.getLockOwner());\n}","handlingStrategy":"validation","validationCode":"ExternalWorkerJob job = cmmnTaskService.createExternalWorkerJobQueryBuilder().externalWorkerJobId(jobId).singleResult();\nif (job != null && !workerId.equals(job.getLockOwner())) throw new IllegalStateException(\"Job locked by \" + job.getLockOwner());","typeGuard":null,"tryCatchPattern":"try { managementService.executeCommand(new ExternalWorkerJobCompleteCmd<>(jobId, workerId, vars)); }\ncatch (FlowableIllegalArgumentException e) { log.warn(\"Lock ownership mismatch on job {}: {}\", jobId, e.getMessage()); }","preventionTips":["Keep one immutable workerId per worker instance in config","Always pass the same workerId used at acquire time to complete/terminate","Check job.getLockOwner() before acting on acquired ids","Log lockOwner mismatches to detect fleet configuration drift"],"tags":["flowable","cmmn","lock-ownership","external-worker-job"],"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-14T05:17:10.506Z"}