flowable/flowable-engine · error · FlowableException
No history job handler registered for type
Error message
No history job handler registered for type
What it means
executeHistoryJobHandler resolves the history job's handler type in the engine's HistoryJobHandler map. If a handler for historyJobEntity.getJobHandlerType() is not found in a non-empty map, Flowable throws this FlowableException. History jobs (e.g. async history processing) need a registered handler to be executed.
Solutions
- Verify async history is configured through the standard ProcessEngineConfigurationImpl (asyncHistoryEnabled=true) so default history handlers register at bootstrap.
- Register custom history handlers via the configuration's history job handler map.
- Check the JOB_HANDLER_TYPE of the failing history job row against registered handler keys.
- Align Flowable versions if history jobs were written by a different engine version.
Example fix
// before // async history on, but engine built manually, handlers missing cfg.setAsyncHistoryEnabled(true); // after cfg.setAsyncHistoryEnabled(true); List<HistoryJobHandler> historyHandlers = new ArrayList<>(); historyHandlers.add(new AsyncHistoryJobHandler()); cfg.setCustomHistoryJobHandlers(historyHandlers); // or rely on default init
Defensive patterns
Strategy: validation
Validate before calling
Map<String, HistoryJobHandler> handlers = jobServiceConfiguration.getHistoryJobHandlers();
if (handlers == null || !handlers.containsKey(historyJob.getJobHandlerType())) {
throw new IllegalStateException("Unregistered history job handler: " + historyJob.getJobHandlerType());
} Try / catch
try { historyJobManager.executeHistoryJob(job); } catch (FlowableException e) { if (e.getMessage().startsWith("No history job handler registered")) { registerHistoryHandlerAndRetry(job); } else throw e; } Prevention
- Enable async history through standard ProcessEngineConfigurationImpl settings.
- Register custom HistoryJobHandlers in engine configuration.
- Check handler-type keys after Flowable version upgrades.
- Monitor logs for handler-registration warnings at bootstrap.
When it happens
Trigger: executeHistoryJob -> executeHistoryJobHandler with a history job whose handler type (typically 'async-history') is absent from jobServiceConfiguration.getHistoryJobHandlers().
Common situations: Enabling async history without the history job handlers being initialized; custom HistoryJobHandler not registered via processEngineConfiguration.setAsyncHistoryEnabled + custom handlers; engines configured with asyncHistoryEnabled but a stripped-down job service configuration.
Understand the failure class
Background: "not installed", "pip install", "required for": how missing-dependency errors surface across open-source libraries — this error's family across 34 libraries.
Related errors
- No job handler registered for type
- Async
- has no job handler type in job config for engine:
- Invalid usage of async_activate job handler, variable scope…
- Invalid usage of job handler, variable scope is of type
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/46ab26f8a261503a.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable-job-service/src/main/java/org/flowable/job/service/impl/asyncexecutor/DefaultJobManager.java:578
} else {
throw new FlowableException("No job handler registered for type " + jobEntity.getJobHandlerType() +
" in job config for engine: " + jobServiceConfiguration.getEngineName() + " for " + jobEntity);
}
} else {
throw new FlowableException(jobEntity + " has no job handler type in job config for engine: " + jobServiceConfiguration.getEngineName());
}
}
protected void executeHistoryJobHandler(HistoryJobEntity historyJobEntity) {
Map<String, HistoryJobHandler> jobHandlers = jobServiceConfiguration.getHistoryJobHandlers();
if (historyJobEntity.getJobHandlerType() != null) {
if (jobHandlers != null) {
HistoryJobHandler jobHandler = jobHandlers.get(historyJobEntity.getJobHandlerType());
if (jobHandler != null) {
jobHandler.execute(historyJobEntity, historyJobEntity.getJobHandlerConfiguration(), getCommandContext(), jobServiceConfiguration);
} else {
throw new FlowableException("No history job handler registered for type " + historyJobEntity.getJobHandlerType() +
" in job config for engine: " + jobServiceConfiguration.getEngineName() + " for " + historyJobEntity);
}
} else {
throw new FlowableException("No history job handler registered for type " + historyJobEntity.getJobHandlerType() +
" in job config for engine: " + jobServiceConfiguration.getEngineName() + " for " + historyJobEntity);
}
} else {
throw new FlowableException("Async " + historyJobEntity + " has no job handler type in job config for engine: " + jobServiceConfiguration.getEngineName());
}
}
protected boolean isValidTime(JobEntity timerEntity, Date newTimerDate, VariableScope variableScope) {
BusinessCalendar businessCalendar = jobServiceConfiguration.getBusinessCalendarManager().getBusinessCalendar(
getBusinessCalendarName(timerEntity, variableScope));
return businessCalendar.validateDuedate(timerEntity.getRepeat(), timerEntity.getMaxIterations(), timerEntity.getEndDate(), newTimerDate);
}View on GitHub (pinned to d6d39ce1c6)