flowable/flowable-engine · error
exception during timer job move for engine {}. Exception mes
Error message
exception during timer job move for engine {}. Exception message: {} What it means
WARN log from the timer-job move step: a non-optimistic-lock Throwable was thrown while moving acquired timer jobs from the timer table to the executable job table. Because the jobs were already acquired (locked), the runnable unlocks them (unlockTimerJobs) before returning so they are not stranded.
Source
Thrown at modules/flowable-job-service/src/main/java/org/flowable/job/service/impl/asyncexecutor/AcquireTimerJobsRunnable.java:236
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) {
logOptimisticLockingException(optimisticLockingException);
unlockTimerJobs(timerJobs); // jobs have been acquired before, so need to unlock when exception happens here
} catch (Throwable t) {
LOGGER.warn("exception during timer job move for engine {}. Exception message: {}", getEngineName(), t.getMessage(), t);
unlockTimerJobs(timerJobs); // jobs have been acquired before, so need to unlock when exception happens here
}
}
protected void logOptimisticLockingException(FlowableOptimisticLockingException optimisticLockingException) {
if (configuration.isGlobalAcquireLockEnabled()) {
LOGGER.warn("Optimistic locking exception (using global acquire lock) for engine {}", getEngineName(), optimisticLockingException);
} else {
LOGGER.debug(
"Optimistic locking exception during async job acquisition. If you have multiple async executors running against the same database, " +
"this exception means that this thread tried to acquire a due async job, which already was acquired by another " +
"async executor acquisition thread.This is expected behavior in a clustered environment. " +
"You can ignore this message if you indeed have multiple async executor acquisition threads running against the same database. " +
"For engine {}. Exception message: {}",
getEngineName(), optimisticLockingException.getMessage());
View on GitHub (pinned to d6d39ce1c6)
Solutions
- Read the chained stack trace for the underlying DB/command error
- Verify the affected jobs were unlocked (the code calls unlockTimerJobs); if stranded locked jobs remain, reset their lock owner/time manually
- Check ACT_RU_TIMER_JOB and ACT_RU_JOB for rows violating constraints (e.g. duplicate IDs)
- Retry the cycle; jobs that were unlocked will be re-acquired
Example fix
// manually unstick stranded locked timer jobs UPDATE ACT_RU_TIMER_JOB SET LOCK_OWNER_ = NULL, LOCK_EXP_TIME_ = NULL WHERE LOCK_OWNER_ = 'dead-node';
Defensive patterns
Strategy: retry
Validate before calling
// detect stranded locked timer jobs periodically SELECT LOCK_OWNER_, COUNT(*) FROM ACT_RU_TIMER_JOB WHERE LOCK_EXP_TIME_ < NOW() GROUP BY LOCK_OWNER_;
Try / catch
try {
moveTimerJobs();
} catch (Throwable t) {
unlockTimerJobs(timerJobs); // always release acquired locks before retry
} Prevention
- Always unlock acquired jobs on failure (as the framework does)
- Check for constraint conflicts between timer and executable job tables
- Monitor for locked jobs with expired lock times
When it happens
Trigger: Exception during executeMoveTimerJobsToExecutableJobs: failure in MoveTimerJobsCmd / bulk move operation, DB errors while writing to ACT_RU_JOB, or version conflicts other than optimistic locking.
Common situations: DB constraint violations during the move; connection failure between acquire and move; duplicated timer job rows after a failed earlier migration.
Related errors
- exception during timer job acquisition for engine {}. Except
- exception for engine {} during async job acquisition: {}
- Error while waiting for global acquire lock for engine {}
- exception during resetting expired jobs: {} for engine {}
- There are app definitions with key = '' and version = ''
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/5cab29ae5665fb32.
Report an issue: GitHub.