flowable/flowable-engine · error · FlowableException
Unable to handle exception
Error message
Unable to handle exception
What it means
When a job fails, ExecuteAsyncRunnable.handleFailedJob records the failure (retries, exception stacktrace, deadletter). If handling itself throws (e.g. the failure-recording command fails, or no exception handler applied), the runnable logs 'Unable to handle exception' and rethrows a wrapping FlowableException. This means the original job error could not be persisted, so retry accounting is incomplete.
Solutions
- Inspect the 'cause' of this exception — it hides the root failure that prevented failure-recording; fix that underlying issue first.
- Check database connectivity/constraints on ACT_RU_JOB / ACT_RU_DEADLETTER_JOB during failure handling.
- Verify the job still exists (not concurrently deleted) when failure handling runs; guard multi-node deployments.
- Ensure custom exception handlers (JobExceptionHandler) do not throw.
Example fix
// before
// failure handling throws, job lost without retry accounting
try {
jobManager.execute(job);
} catch (Exception e) {
// swallow
}
// after
try {
jobManager.execute(job);
} catch (FlowableException e) {
LOGGER.error("Job execution and failure handling failed", e.getCause() != null ? e.getCause() : e);
// inspect cause: DB state / handler errors
} Defensive patterns
Strategy: try-catch
Try / catch
try {
jobManager.execute(job);
} catch (FlowableException e) {
Throwable root = e.getCause() != null ? e.getCause() : e;
LOGGER.error("Job failure handling failed; root cause: {}", root, root);
// inspect root: DB state, concurrent deletion, serializer errors
} Prevention
- Always log and inspect the cause chain of this error.
- Monitor DB health on job tables; failures here often follow DB outages.
- Avoid concurrent deletion of jobs while the executor processes them in clustered setups.
- Keep custom JobExceptionHandler implementations exception-free.
When it happens
Trigger: executeJob catches the job's original exception and calls handleFailedJob, but the failure-handling path (decrementing retries, saving the exception stacktrace, moving to deadletter) throws — e.g. DB constraint issues, transaction problems, or a null original exception message path — and ExecuteAsyncRunnable.java:305 wraps it.
Common situations: Database outages or lock contention while persisting job failure info; custom JobExceptionHandler throwing; serializer failing to persist the exception stacktrace column; jobs deleted concurrently by another node while being failed.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- couldn't deduct database type from database product name
- couldn't get db schema version
- Error adding model editor source extra
- Error adding model editor source extra
- Error setting non null for parameter #
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/f620a09f377ec86e.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable-job-service/src/main/java/org/flowable/job/service/impl/asyncexecutor/ExecuteAsyncRunnable.java:305
});
}
});
return;
}
for (AsyncRunnableExecutionExceptionHandler asyncRunnableExecutionExceptionHandler : asyncRunnableExecutionExceptionHandlers) {
if (asyncRunnableExecutionExceptionHandler.handleException(this.jobServiceConfiguration, this.job, exception)) {
// Needs to run in a separate transaction as the original transaction has been marked for rollback
unlockJobIfNeeded();
return;
}
}
LOGGER.error("Unable to handle exception {} for job {}.", exception, job);
throw new FlowableException("Unable to handle exception " + exception.getMessage() + " for " + job + ".", exception);
}
}
View on GitHub (pinned to d6d39ce1c6)