flowable/flowable-engine · error · FlowableException
No job handler registered for type
Error message
No job handler registered for type
What it means
executeJobHandler looks up the job's handler type in the engine's registered JobHandler map. When the map is populated but the specific jobEntity.getJobHandlerType() has no matching handler, Flowable throws this FlowableException naming the type and engine. Every job created must have a handler registered in the process engine configuration.
Solutions
- Register the missing handler: add it to the process engine configuration (ProcessEngineConfigurationImpl.setCustomJobHandlers or the job service configuration's job handler map).
- Verify the jobHandlerType string stored on the job row against the keys of the registered jobHandlers map.
- Align engine versions if jobs were created by a different Flowable version (check ACT_RU_JOB JOB_HANDLER_TYPE values).
- Check for errors in engine bootstrap logs indicating handler registration failures.
Example fix
// before
ProcessEngineConfiguration cfg = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("flowable.cfg.xml");
// custom handler never registered
// after
ProcessEngineConfigurationImpl cfg = (ProcessEngineConfigurationImpl)
ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("flowable.cfg.xml");
List<JobHandler> handlers = new ArrayList<>();
handlers.add(new MyCustomJobHandler());
cfg.setCustomJobHandlers(handlers); Defensive patterns
Strategy: validation
Validate before calling
Map<String, JobHandler> handlers = jobServiceConfiguration.getJobHandlers();
if (handlers == null || !handlers.containsKey(job.getJobHandlerType())) {
throw new IllegalStateException("Unregistered job handler type: " + job.getJobHandlerType());
} Try / catch
try { jobManager.executeMessageJob(job); } catch (FlowableException e) { if (e.getMessage().startsWith("No job handler registered")) { registerMissingHandlerAndRetry(job); } else throw e; } Prevention
- Register all custom JobHandlers in process engine configuration at bootstrap.
- Keep job-handler registrations in one configuration module shared by all nodes.
- Verify handler keys match jobHandlerType strings in the job tables after upgrades.
- Smoke-test engine startup by executing one job of each handler type.
When it happens
Trigger: A JobEntity with a jobHandlerType (e.g. 'async-continuation', 'timer', custom handler) that is not present in the JobServiceConfiguration's jobHandlers map when executeMessageJob or executeTimerJob runs the job.
Common situations: Custom JobHandler implementations not registered via the engine configuration's customJobHandlers; jobs persisted by a newer/other Flowable version whose handler types the current engine does not know; classpath issues dropping handler registrations.
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 history job handler registered for type
- A channel key detection value is required for the channel…
- A datasource is required for initializing the engine
- Builder is missing constructor (can't pass features)
- Cannot migrate process(es), not enough information
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/1a421566919d2535.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable-job-service/src/main/java/org/flowable/job/service/impl/asyncexecutor/DefaultJobManager.java:556
protected void executeJobHandler(JobEntity jobEntity) {
VariableScope variableScope = null;
if (jobServiceConfiguration.getInternalJobManager() != null) {
variableScope = jobServiceConfiguration.getInternalJobManager().resolveVariableScope(jobEntity);
}
if (variableScope == null) {
variableScope = NoExecutionVariableScope.getSharedInstance();
}
Map<String, JobHandler> jobHandlers = jobServiceConfiguration.getJobHandlers();
if (jobEntity.getJobHandlerType() != null) {
if (jobHandlers != null) {
JobHandler jobHandler = jobHandlers.get(jobEntity.getJobHandlerType());
if (jobHandler != null) {
jobHandler.execute(jobEntity, jobEntity.getJobHandlerConfiguration(), variableScope, getCommandContext());
} 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("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());View on GitHub (pinned to d6d39ce1c6)