flowable/flowable-engine · error · FlowableOptimisticLockingException

Could not lock case instance

Error message

Could not lock case instance

What it means

Thrown by MybatisCaseInstanceDataManagerImpl.updateLockTime when the directUpdate on updateCaseInstanceLockTime affects 0 rows. This is an optimistic-locking style failure: the engine tried to acquire a lock on a case instance but the row no longer matches the expected state (deleted or concurrently locked).

Source

Thrown at modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/persistence/entity/data/impl/MybatisCaseInstanceDataManagerImpl.java:198

    }

    @Override
    public long countByCriteria(CaseInstanceQueryImpl query) {
        setSafeInValueLists(query);
        return (Long) getDbSqlSession().selectOne("selectCaseInstanceCountByQueryCriteria", query);
    }

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

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

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

    @Override
    public void clearAllLockTimes(String lockOwner) {
        HashMap<String, Object> params = new HashMap<>();
        params.put("lockOwner", lockOwner);
        getDbSqlSession().directUpdate("clearAllCaseInstanceLockTimes", params);
    }

    protected void setSafeInValueLists(CaseInstanceQueryImpl caseInstanceQuery) {

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Retry the operation after re-fetching the case instance to confirm it still exists
  2. Catch FlowableOptimisticLockingException and treat it as 'another node holds the lock', rescheduling the work
  3. Verify the case instance was not concurrently deleted before attempting to lock

Example fix

// before
entityManager.updateLockTime(caseInstanceId, lockOwner, lockDate, expirationTime); // throws if row gone
// after
try {
    entityManager.updateLockTime(caseInstanceId, lockOwner, lockDate, expirationTime);
} catch (FlowableOptimisticLockingException e) {
    // another node locked or the instance was removed; reschedule or skip
}
Defensive patterns

Strategy: retry

Try / catch

try {
    entityManager.updateLockTime(caseInstanceId, lockOwner, lockDate, expirationTime);
} catch (FlowableOptimisticLockingException e) {
    // re-fetch instance; retry with backoff or skip if deleted
}

Prevention

When it happens

Trigger: Calling updateLockTime(caseInstanceId, ...) when the case instance row has already been deleted or its LOCK_TIME_/LOCK_OWNER_/EXPIRATION_TIME_ state changed concurrently so the conditional update matched nothing.

Common situations: Long-running or heavily concurrent case operations where two threads race for the lock; stale entity references after a case instance was terminated and removed; distributed engine nodes with unsynchronized caches.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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