flowable/flowable-engine · error · FlowableException

has no job handler type in job config for engine:

Error message

 has no job handler type in job config for engine: 

What it means

executeJobHandler requires every JobEntity to carry a jobHandlerType string that selects its JobHandler. When the entity's jobHandlerType is null, Flowable throws this FlowableException stating the job has no handler type. Without the type the engine cannot know what code to run for the job.

Solutions

  1. Always set the job handler type when creating jobs: jobEntity.setJobHandlerType("async-continuation") (or the appropriate type).
  2. Fix existing rows by updating JOB_HANDLER_TYPE for the offending job, or delete the orphan job row.
  3. If migrating jobs between environments, include the handler-type columns in the migration script.
  4. Add a pre-save validation that jobHandlerType is non-null for all created job entities.

Example fix

// before
MessageJobEntity job = jobService.createMessageJob();
job.setJobHandlerConfiguration("cfg");
// jobHandlerType never set

// after
MessageJobEntity job = jobService.createMessageJob();
job.setJobHandlerType(AsyncContinuationJobHandler.TYPE); // e.g. "async-continuation"
job.setJobHandlerConfiguration("cfg");
Defensive patterns

Strategy: validation

Validate before calling

if (job.getJobHandlerType() == null || job.getJobHandlerType().isEmpty()) {
    throw new IllegalStateException("Job " + job.getId() + " has no jobHandlerType");
}

Type guard

boolean hasHandlerType(JobEntity job) { return job.getJobHandlerType() != null && !job.getJobHandlerType().isEmpty(); }

Try / catch

try { jobManager.executeMessageJob(job); } catch (FlowableException e) { if (e.getMessage().endsWith("has no job handler type")) { repairOrDeleteJobRow(job.getId()); } else throw e; }

Prevention

When it happens

Trigger: A JobEntity row (e.g. in ACT_RU_JOB) with NULL JOB_HANDLER_TYPE is passed to executeMessageJob/executeTimerJob -> executeJobHandler.

Common situations: Rows hand-inserted or migrated into the job tables without JOB_HANDLER_TYPE; custom job creation code that forgets setJobHandlerType(); data corruption or schema mismatches after upgrades.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11). Data as JSON: /api/errors/3a43ef59b1a747b7. Report an issue: GitHub.

Appendix: source

Thrown at modules/flowable-job-service/src/main/java/org/flowable/job/service/impl/asyncexecutor/DefaultJobManager.java:566

        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());
                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);

View on GitHub (pinned to d6d39ce1c6)