flowable/flowable-engine · error · FlowableException
Could not acquire lock " + lockName + ". Current lock…
Error message
Could not acquire lock " + lockName + ". Current lock value: " + lockValue
What it means
LockManagerImpl.waitForLock uses optimistic locking on a shared lock row (per lockName/engineType). If it cannot acquire the lock within the configured wait/attempt budget, it reads the current lock value with GetLockValueCmd and throws FlowableException naming the lock and its current holder value. This typically means another node/thread holds the lock longer than the wait allows, or the lock row was left behind by a crashed owner.
Solutions
- Increase the lock wait time / lockForceAcquireAfter configuration so long operations can complete
- Investigate the current lock value printed in the message — identify which node holds it and whether it is stale (dead node)
- Restart/reconcile the environment: if the holder is dead, clear the stale lock row or force-acquire by configuration; ensure all nodes run the same Flowable version
Example fix
// before (default too short) engineConfig.setLockWaitTime(10_000); // after engineConfig.setLockWaitTime(60_000); engineConfig.setLockForceAcquireAfter(120_000);
Defensive patterns
Strategy: retry
Validate before calling
// before waiting, check lock holder String holder = commandExecutor.execute(new GetLockValueCmd(lockName, engineType)); boolean stale = holder == null || lastHeartbeatOlderThan(holder, lockForceAcquireAfter);
Try / catch
try { lockManager.waitForLock(lockName); } catch (FlowableException e) { if (e.getMessage().startsWith("Could not acquire lock")) { alertOps(e.getMessage()); retryWithBackoff(); } else throw e; } Prevention
- Tune lockWaitTime and lockForceAcquireAfter to exceed the longest protected operation
- Monitor the lock table and alert on stale lock values
- Ensure nodes shut down gracefully so locks are released; pin schema-migration locks to one node
When it happens
Trigger: Multiple engine nodes competing for the same lock (e.g. async executor, history cleanup, schema upgrade) where the holder keeps refreshing the lock past lockForceAcquireAfter; a crashed node left the lock value not reset; misconfigured wait time too short for the operation duration.
Common situations: Cluster deployments where one node runs a long history-cleanup/job-acquisition while another waits; after an abrupt kill of the lock-holding node without releasing; lock table (ACT_GE_PROPERTY / engine property) rows out of sync.
Understand the failure class
Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.
Related errors
- Cannot delete job when the job is being executed. Try again…
- does not hold a lock on the requested job
- already taking a transition
- Can only enable a plan item instance which is in state…
- Cannot delete when the job is being executed. Try again…
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/653de846ecb21cd3.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/lock/LockManagerImpl.java:85
@Override
public void waitForLock(Duration waitTime) {
long timeToGiveUp = System.currentTimeMillis()+ waitTime.toMillis();
boolean locked = false;
while (!locked && (System.currentTimeMillis() < timeToGiveUp)) {
locked = acquireLock();
if (!locked) {
try {
Thread.sleep(getLockPollRate().toMillis());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
if (!locked) {
String lockValue = executeCommand(new GetLockValueCmd(lockName, engineType));
throw new FlowableException("Could not acquire lock " + lockName + ". Current lock value: " + lockValue);
}
}
@Override
public boolean acquireLock() {
return acquireLock(lockForceAcquireAfter);
}
@Override
public boolean acquireLock(Duration lockForceAcquireAfter) {
if (hasAcquiredLock) {
return true;
}
try {
hasAcquiredLock = executeCommand(new LockCmd(lockName, lockForceAcquireAfter, engineType));
if (hasAcquiredLock) {
LOGGER.debug("Successfully acquired lock {}", lockName);View on GitHub (pinned to d6d39ce1c6)