flowable/flowable-engine · error
exception for engine
Error message
exception for engine {} during async job acquisition: {} What it means
Generic WARN log from the async job acquisition thread: any Throwable thrown during acquireAndExecuteJobs that is not an optimistic-locking exception is caught and logged here with the engine name and message. It signals the acquisition cycle failed (e.g. DB error) and the thread will sleep before retrying.
Solutions
- Read the chained stack trace in the log to identify the root cause (usually a SQL/DB error)
- Verify database connectivity and connection pool health
- Check that the ACT_RU_JOB schema matches your Flowable version (run the DB upgrade scripts)
- If persistent, inspect the failing command/query and any custom session factories
Example fix
// before: acquisition thread repeatedly fails on dead pool connections
// after: validate connections and cap retries
dataSource.setDefaultTestOnBorrow(true);
// e.g. Hikari: hikariConfig.setConnectionTestQuery("SELECT 1"); Defensive patterns
Strategy: try-catch
Validate before calling
// health-check DB before starting the engine
try (Connection c = dataSource.getConnection()) { c.isValid(5); } catch (SQLException e) { fail("database unreachable"); } Try / catch
try {
executor.start();
} catch (DataAccessException | SQLException e) {
log.error("job acquisition failed due to DB issue", e);
// alert / backoff / retry cycle
} Prevention
- Monitor database connectivity and pool saturation
- Keep schema in sync with Flowable version via upgrade scripts
- Add alerts on repeated acquisition-failure log lines
- Use validated/test-on-borrow pooled connections
When it happens
Trigger: Any non-optimistic-lock Throwable during job acquisition: database connectivity loss, SQL errors, deadlock, command execution failure inside AcquireAsyncJobsDueCmd.
Common situations: Transient DB outages or failovers in clustered environments; connection pool exhaustion; schema mismatch after a Flowable version upgrade (missing job tables/columns).
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 during timer job acquisition for engine
- exception during resetting expired jobs
- exception during timer job move for engine
- Optimistic locking exception (using global acquire lock)…
- Cannot delete
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/08246c873280414f.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable-job-service/src/main/java/org/flowable/job/service/impl/asyncexecutor/AcquireAsyncJobsDueRunnable.java:220
} catch (FlowableOptimisticLockingException optimisticLockingException) {
lifecycleListener.optimistLockingException(getEngineName(), asyncExecutor.getMaxAsyncJobsDuePerAcquisition());
if (globalAcquireLockEnabled) {
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());
}
} catch (Throwable e) {
LOGGER.warn("exception for engine {} during async job acquisition: {}", getEngineName(), e.getMessage(), e);
}
return asyncExecutor.getDefaultAsyncJobAcquireWaitTimeInMillis();
}
protected List<JobInfoEntity> offerJobs(List<? extends JobInfoEntity> acquiredJobs) {
List<JobInfoEntity> rejected = new ArrayList<>();
for (JobInfoEntity job : acquiredJobs) {
boolean jobSuccessFullyOffered = asyncExecutor.executeAsyncJob(job);
if (!jobSuccessFullyOffered) {
rejected.add(job);
}
}
return rejected;
}
public void stop() {
synchronized (MONITOR) {View on GitHub (pinned to d6d39ce1c6)