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

  1. Examine the full stack trace logged with this message for the root cause
  2. Check DB connectivity and the ACT_RU_TIMER_JOB table schema for your Flowable version
  3. Re-run Flowable's database upgrade scripts if versions are mismatched
  4. 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

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


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