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

  1. Inspect the 'cause' of this exception — it hides the root failure that prevented failure-recording; fix that underlying issue first.
  2. Check database connectivity/constraints on ACT_RU_JOB / ACT_RU_DEADLETTER_JOB during failure handling.
  3. Verify the job still exists (not concurrently deleted) when failure handling runs; guard multi-node deployments.
  4. 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

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


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)