flowable/flowable-engine · error
exception during timer job acquisition for engine {}. Except
Error message
exception during timer job acquisition for engine {}. Exception message: {} What it means
Generic WARN log from the timer-job acquisition thread: a Throwable other than FlowableOptimisticLockingException was thrown while acquiring due timer jobs (AcquireTimerJobsCmd). The wait time falls back to the default timer job acquire wait, and the acquisition loop stops/restarts via lifecycleListener.stopAcquiring.
Source
Thrown at modules/flowable-job-service/src/main/java/org/flowable/job/service/impl/asyncexecutor/AcquireTimerJobsRunnable.java:213
if (globalAcquireLockEnabled) {
// Always wait when running with global acquire lock, to let other nodes have the ability to fill the queue
// If 0 was returned, it means there is still work to do, but we want to give other nodes a chance.
millisToWait = configuration.getLockPollRate().toMillis();
} else {
// Otherwise (no global acquire lock),the node can retry immediately
millisToWait = 0;
}
}
} catch (FlowableOptimisticLockingException optimisticLockingException) {
logOptimisticLockingException(optimisticLockingException);
} catch (Throwable e) {
LOGGER.warn("exception during timer job acquisition for engine {}. Exception message: {}", getEngineName(), e.getMessage(), e);
millisToWait = asyncExecutor.getDefaultTimerJobAcquireWaitTimeInMillis();
}
lifecycleListener.stopAcquiring(getEngineName());
return millisToWait;
}
protected void executeMoveTimerJobsToExecutableJobs(List<TimerJobEntity> timerJobs) {
try {
if (configuration.isGlobalAcquireLockEnabled()) {
commandExecutor.execute(new BulkMoveTimerJobsToExecutableJobsCmd(jobManager, timerJobs));
} else {
commandExecutor.execute(new MoveTimerJobsToExecutableJobsCmd(jobManager, timerJobs));
}
} catch (FlowableOptimisticLockingException optimisticLockingException) {View on GitHub (pinned to d6d39ce1c6)
Solutions
- Examine the full stack trace logged with this message for the root cause
- Check DB connectivity and the ACT_RU_TIMER_JOB table schema for your Flowable version
- Re-run Flowable's database upgrade scripts if versions are mismatched
- Restart/redeploy the engine if the acquisition thread has stopped acquiring
Example fix
// before: schema mismatch after upgrading the jar without migrating DB // after: run the upgrade // mvn flowable:upgrade or execute org.flowable.db.upgrade scripts for your version
Defensive patterns
Strategy: try-catch
Validate before calling
// schema sanity check on startup SELECT COUNT(*) FROM ACT_RU_TIMER_JOB; -- must not throw SQLException
Try / catch
try {
timerJobs = commandExecutor.execute(new AcquireTimerJobsCmd(cfg));
} catch (Throwable e) {
log.warn("timer acquisition failed; backing off", e);
Thread.sleep(defaultWaitMillis);
} Prevention
- Run DB upgrade scripts when upgrading Flowable
- Watch DB health during failovers
- Alert on repeated 'exception during timer job acquisition' logs
When it happens
Trigger: Any non-optimistic-lock exception during timer job acquisition: SQL errors, connection loss, command failures in AcquireTimerJobsCmd, serialization issues reading timer job entities.
Common situations: Database failover mid-acquisition; schema drift between engine version and ACT_RU_TIMER_JOB table; corrupted job entities failing to hydrate.
Understand the failure class
Background: Database query failed: Internal Server Error 500s wrapping SQL, Prisma, and connection failures — what to check first — this error's family across 16 libraries.
Related errors
- exception for engine {} during async job acquisition: {}
- exception during timer job move for engine {}. Exception mes
- Optimistic locking exception (using global acquire lock) for
- Error while waiting for global acquire lock for engine {}
- exception during resetting expired jobs: {} for engine {}
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/e91474efed3ac91f.
Report an issue: GitHub.