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

  1. Verify async history is configured through the standard ProcessEngineConfigurationImpl (asyncHistoryEnabled=true) so default history handlers register at bootstrap.
  2. Register custom history handlers via the configuration's history job handler map.
  3. Check the JOB_HANDLER_TYPE of the failing history job row against registered handler keys.
  4. 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

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


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)