{"record":{"id":"9d24ccea11e1dbd0","repo":"flowable/flowable-engine","slug":"error-while-waiting-for-global-acquire-lock-for-en-9d24cc","errorCode":null,"errorMessage":"Error while waiting for global acquire lock for engine {}","messagePattern":"Error while waiting for global acquire lock for engine (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"modules/flowable-job-service/src/main/java/org/flowable/job/service/impl/asyncexecutor/AcquireTimerJobsRunnable.java","lineNumber":173,"sourceCode":"        long millisToWait = 0L;\n\n        try {\n\n            boolean globalAcquireLockEnabled = configuration.isGlobalAcquireLockEnabled();\n            if (globalAcquireLockEnabled) {\n\n                // When running with global acquire lock, we only need to have the lock during the acquire.\n                // In the move phase, other nodes can already acquire timer jobs themselves (as the lock is free).\n                try {\n                    timerJobs = lockManager.waitForLockRunAndRelease(configuration.getLockWaitTime(), () -> {\n                        return commandExecutor.execute(new AcquireTimerJobsWithGlobalAcquireLockCmd(asyncExecutor));\n                    });\n\n                } catch (Exception e) {\n                    // Don't do anything, lock will be tried again next time\n\n                    if (!(e instanceof FlowableException)) { // FlowableException doesn't need to be logged, could be regular lock logic\n                        LOGGER.warn(\"Error while waiting for global acquire lock for engine {}\", getEngineName(), e);\n                    }\n                }\n\n            } else {\n                timerJobs = commandExecutor.execute(new AcquireTimerJobsCmd(asyncExecutor));\n\n            }\n\n            if (!timerJobs.isEmpty()) {\n                List<TimerJobEntity> finalTimerJobs = timerJobs;\n                moveTimerJobsExecutorService.execute(() -> {\n                    executeMoveTimerJobsToExecutableJobs(finalTimerJobs);\n                });\n            }\n\n            // if all jobs were executed\n            millisToWait = asyncExecutor.getDefaultTimerJobAcquireWaitTimeInMillis();\n            int nrOfJobsAcquired = timerJobs.size();","sourceCodeStart":155,"sourceCodeEnd":191,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-job-service/src/main/java/org/flowable/job/service/impl/asyncexecutor/AcquireTimerJobsRunnable.java#L155-L191","documentation":"WARN log from the timer-job acquisition runnable while waiting for the global acquire lock: a non-FlowableException was thrown during the lock-wait phase and it is logged because it likely indicates a real problem, not the regular (expected) lock-contention path. The cycle simply continues and retries the lock next round.","triggerScenarios":"Acquiring the global lock (e.g. via the lock manager/command) throws a non-Flowable exception — infrastructure failures, JDBC errors from the lock table, or a custom lock manager raising unexpected exceptions.","commonSituations":"Lock table missing or unreachable; database outage during lock acquisition; custom FlowableLockManager implementations throwing raw RuntimeExceptions.","solutions":["Inspect the chained exception to find why the lock wait failed","Verify the lock manager's storage (e.g. ACT_RU_ENTITYLINK/lock table or external store) exists and is reachable","Ensure custom lock managers only throw FlowableException for expected lock contention","Retry — the runnable deliberately retries the lock on the next acquisition cycle"],"exampleFix":"// before\nclass MyLockManager implements FlowableLockManager { public void acquireLock(...) { throw new RuntimeException(\"lock busy\"); } }\n// after\npublic void acquireLock(...) { if (locked) { throw new FlowableException(\"lock busy\"); } }","handlingStrategy":"try-catch","validationCode":"// verify lock table reachability before enabling global lock\ntry (Connection c = dataSource.getConnection(); Statement s = c.createStatement()) { s.executeQuery(\"SELECT 1 FROM ACT_RU_JOB LIMIT 1\"); }","typeGuard":null,"tryCatchPattern":"try {\n    lockManager.acquireLock(...);\n} catch (FlowableException expectedLockContention) {\n    // normal: retry next cycle\n} catch (RuntimeException e) {\n    log.warn(\"lock wait infrastructure failure\", e);\n}","preventionTips":["Ensure custom FlowableLockManager throws only FlowableException for contention","Verify lock storage exists and is shared cluster-wide","Monitor lock table latency"],"tags":["flowable","timer-jobs","global-lock","lock-manager","async-executor"],"backgroundTag":"database-query-failed","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"}