flowable/flowable-engine · warning · FlowableOptimisticLockingException

Could not lock process instance

Error message

Could not lock process instance

What it means

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.

Source

Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/persistence/entity/data/impl/MybatisExecutionDataManager.java:311

    @Override
    public void updateExecutionTenantIdForDeployment(String deploymentId, String newTenantId) {
        HashMap<String, Object> params = new HashMap<>();
        params.put("deploymentId", deploymentId);
        params.put("tenantId", newTenantId);
        getDbSqlSession().directUpdate("updateExecutionTenantIdForDeployment", params);
    }

    @Override
    public void updateProcessInstanceLockTime(String processInstanceId, Date lockDate, String lockOwner, Date expirationTime) {
        HashMap<String, Object> params = new HashMap<>();
        params.put("id", processInstanceId);
        params.put("lockTime", lockDate);
        params.put("expirationTime", expirationTime);
        params.put("lockOwner", lockOwner);

        int result = getDbSqlSession().directUpdate("updateProcessInstanceLockTime", params);
        if (result == 0) {
            throw new FlowableOptimisticLockingException("Could not lock process instance");
        }
    }

    @Override
    public void updateAllExecutionRelatedEntityCountFlags(boolean newValue) {
        getDbSqlSession().directUpdate("updateExecutionRelatedEntityCountEnabled", newValue);
    }

    @Override
    public void clearProcessInstanceLockTime(String processInstanceId) {
        HashMap<String, Object> params = new HashMap<>();
        params.put("id", processInstanceId);
        getDbSqlSession().directUpdate("clearProcessInstanceLockTime", params);
    }

    @Override
    public void clearAllProcessInstanceLockTimes(String lockOwner) {
        HashMap<String, Object> params = new HashMap<>();

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. 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.
  2. Ensure async executor configuration (exclusive lock settings, asyncExecutorActivate) matches a single or properly clustered setup.
  3. Verify the process instance still exists before locking and handle the case where it completed concurrently.
  4. Check that all engine nodes use the same database and consistent lock timeout configuration.

Example fix

// before
managementService.executeCommand(new LockProcessInstanceCmd(instanceId, owner, duration)); // throws on conflict
// after
int attempts = 3;
while (attempts-- > 0) {
    try { managementService.executeCommand(cmd); break; }
    catch (FlowableOptimisticLockingException e) { /* backoff and retry */ }
}
Defensive patterns

Strategy: retry

Validate before calling

boolean exists = runtimeService.createProcessInstanceQuery().processInstanceId(instanceId).count() > 0;
if (!exists) return; // instance already gone; skip lock

Try / catch

try { /* acquire lock */ } catch (FlowableOptimisticLockingException e) { Thread.sleep(backoff); retry(); }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11). Data as JSON: /api/errors/27885da7a65719b2. Report an issue: GitHub.