{"record":{"id":"27885da7a65719b2","repo":"flowable/flowable-engine","slug":"could-not-lock-process-instance","errorCode":null,"errorMessage":"Could not lock process instance","messagePattern":"Could not lock process instance","errorType":"exception","errorClass":"FlowableOptimisticLockingException","httpStatus":null,"severity":"warning","filePath":"modules/flowable-engine/src/main/java/org/flowable/engine/impl/persistence/entity/data/impl/MybatisExecutionDataManager.java","lineNumber":311,"sourceCode":"    @Override\n    public void updateExecutionTenantIdForDeployment(String deploymentId, String newTenantId) {\n        HashMap<String, Object> params = new HashMap<>();\n        params.put(\"deploymentId\", deploymentId);\n        params.put(\"tenantId\", newTenantId);\n        getDbSqlSession().directUpdate(\"updateExecutionTenantIdForDeployment\", params);\n    }\n\n    @Override\n    public void updateProcessInstanceLockTime(String processInstanceId, Date lockDate, String lockOwner, Date expirationTime) {\n        HashMap<String, Object> params = new HashMap<>();\n        params.put(\"id\", processInstanceId);\n        params.put(\"lockTime\", lockDate);\n        params.put(\"expirationTime\", expirationTime);\n        params.put(\"lockOwner\", lockOwner);\n\n        int result = getDbSqlSession().directUpdate(\"updateProcessInstanceLockTime\", params);\n        if (result == 0) {\n            throw new FlowableOptimisticLockingException(\"Could not lock process instance\");\n        }\n    }\n\n    @Override\n    public void updateAllExecutionRelatedEntityCountFlags(boolean newValue) {\n        getDbSqlSession().directUpdate(\"updateExecutionRelatedEntityCountEnabled\", newValue);\n    }\n\n    @Override\n    public void clearProcessInstanceLockTime(String processInstanceId) {\n        HashMap<String, Object> params = new HashMap<>();\n        params.put(\"id\", processInstanceId);\n        getDbSqlSession().directUpdate(\"clearProcessInstanceLockTime\", params);\n    }\n\n    @Override\n    public void clearAllProcessInstanceLockTimes(String lockOwner) {\n        HashMap<String, Object> params = new HashMap<>();","sourceCodeStart":293,"sourceCodeEnd":329,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-engine/src/main/java/org/flowable/engine/impl/persistence/entity/data/impl/MybatisExecutionDataManager.java#L293-L329","documentation":"updateProcessInstanceLockTime issues a direct SQL update to acquire a process instance lock (used for async job acquisition / exclusive job locking). If the update affects 0 rows, meaning the process instance row no longer matches (deleted, or lock already taken/changed by another node), a FlowableOptimisticLockingException is thrown.","triggerScenarios":"Async executor acquiring/locking a process instance lock where the ACT_RU_EXECUTION row was deleted or its lock columns changed between read and update; concurrent job acquisition on multiple engine nodes competing for the same instance lock.","commonSituations":"Clustered Flowable deployments with multiple async executors racing on the same process instance; process instance finishing/being deleted while a lock was being acquired; lock expiration time changed by another thread.","solutions":["Retry the operation — this is an optimistic-locking conflict; the async executor normally retries automatically, so in custom code wrap the command in a retry with backoff.","Ensure async executor configuration (exclusive lock settings, asyncExecutorActivate) matches a single or properly clustered setup.","Verify the process instance still exists before locking and handle the case where it completed concurrently.","Check that all engine nodes use the same database and consistent lock timeout configuration."],"exampleFix":"// before\nmanagementService.executeCommand(new LockProcessInstanceCmd(instanceId, owner, duration)); // throws on conflict\n// after\nint attempts = 3;\nwhile (attempts-- > 0) {\n    try { managementService.executeCommand(cmd); break; }\n    catch (FlowableOptimisticLockingException e) { /* backoff and retry */ }\n}","handlingStrategy":"retry","validationCode":"boolean exists = runtimeService.createProcessInstanceQuery().processInstanceId(instanceId).count() > 0;\nif (!exists) return; // instance already gone; skip lock","typeGuard":null,"tryCatchPattern":"try { /* acquire lock */ } catch (FlowableOptimisticLockingException e) { Thread.sleep(backoff); retry(); }","preventionTips":["Use retry with backoff for lock acquisition in clustered setups","Keep async executor configuration consistent across nodes","Verify instance existence before locking","Don't hold instance locks longer than the expiration window"],"tags":["optimistic-locking","concurrency","async-executor"],"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-14T11:17:12.474Z"}