{"record":{"id":"ef22b7b152423076","repo":"flowable/flowable-engine","slug":"could-not-lock-process-instance-ef22b7","errorCode":null,"errorMessage":"Could not lock process instance","messagePattern":"Could not lock process instance","errorType":"exception","errorClass":"ActivitiOptimisticLockingException","httpStatus":null,"severity":"warning","filePath":"modules/flowable5-engine/src/main/java/org/activiti/engine/impl/persistence/entity/ExecutionEntityManager.java","lineNumber":226,"sourceCode":"        getDbSqlSession().update(\"updateExecutionTenantIdForDeployment\", params);\n    }\n\n    public void updateProcessInstanceLockTime(String processInstanceId) {\n        CommandContext commandContext = Context.getCommandContext();\n        Date expirationTime = commandContext.getProcessEngineConfiguration().getClock().getCurrentTime();\n        int lockMillis = commandContext.getProcessEngineConfiguration().getAsyncExecutorAsyncJobLockTimeInMillis();\n        GregorianCalendar lockCal = new GregorianCalendar();\n        lockCal.setTime(expirationTime);\n        lockCal.add(Calendar.MILLISECOND, lockMillis);\n\n        HashMap<String, Object> params = new HashMap<>();\n        params.put(\"id\", processInstanceId);\n        params.put(\"lockTime\", lockCal.getTime());\n        params.put(\"expirationTime\", expirationTime);\n\n        int result = getDbSqlSession().update(\"updateProcessInstanceLockTime\", params);\n        if (result == 0) {\n            throw new ActivitiOptimisticLockingException(\"Could not lock process instance\");\n        }\n    }\n\n    public void clearProcessInstanceLockTime(String processInstanceId) {\n        HashMap<String, Object> params = new HashMap<>();\n        params.put(\"id\", processInstanceId);\n\n        getDbSqlSession().update(\"clearProcessInstanceLockTime\", params);\n    }\n\n}\n","sourceCodeStart":208,"sourceCodeEnd":238,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable5-engine/src/main/java/org/activiti/engine/impl/persistence/entity/ExecutionEntityManager.java#L208-L238","documentation":"The engine uses optimistic DB locking to grant exclusive async-job acquisition to one executor node. updateProcessInstanceLockTime issues an UPDATE of the lock columns; if the update affects 0 rows, the expected process instance row was not in the state assumed (deleted, or lock version changed) and ActivitiOptimisticLockingException is thrown.","triggerScenarios":"Async executor/ job acquisition calls updateProcessInstanceLockTime for a process instance that was deleted concurrently by another transaction, or whose exclusive-lock row changed between read and update; multiple job executors racing for the same exclusive job.","commonSituations":"Clustered deployments with several async executor nodes contending for jobs; jobs whose process instance was deleted while still queued; stale job entries surviving instance deletion in older versions.","solutions":["Treat this as a transient contention signal: re-fetch the job/process instance and retry the acquisition if it still exists.","Verify the job's process instance still exists; if not, remove/expire the stale job rather than locking.","Reduce executor contention: tune async executor lock time and pool size so fewer nodes grab the same jobs simultaneously.","Upgrade to a Flowable version where job acquisition handles vanished instances gracefully (job is skipped instead of failing)."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"Job job = managementService.createJobQuery().jobId(jobId).singleResult();\nif (job == null || job.getProcessInstanceId() == null) return; // stale job, skip lock attempt","typeGuard":null,"tryCatchPattern":"try {\n    acquireAndLock(instanceId);\n} catch (ActivitiOptimisticLockingException e) {\n    // another executor won the race or instance vanished: back off and retry once\n    Thread.sleep(retryBackoffMs);\n    retryAcquisition(instanceId);\n}","preventionTips":["Tune async executor lock time and pool size in clustered setups","Purge stale jobs whose process instances no longer exist","Keep engine versions consistent across all cluster nodes"],"tags":["optimistic-locking","async-executor","job-acquisition","cluster"],"backgroundTag":"optimistic-locking-conflict","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"}