flowable/flowable-engine · error · FlowableException

Only jobs with type JobEntity are supported to be executed…

Error message

Only jobs with type JobEntity are supported to be executed. It was 

What it means

DefaultJobManager.execute(Job) only knows how to execute JobEntity instances. If the async executor hands it a JobInfo (or any other non-JobEntity job implementation) it cannot be cast to a message or timer JobEntity, so Flowable throws this FlowableException. It is an internal contract violation between the async executor's job acquisition and the job manager.

Solutions

  1. Ensure the object passed to jobManager.execute(job) is a JobEntity instance (e.g. the loaded MessageJobEntity/TimerJobEntity), not a JobInfo wrapper.
  2. If using a custom async executor, fetch the actual JobEntity from the job entity manager before calling execute().
  3. Check Flowable version compatibility between the engine and any custom async-executor integration code.

Example fix

// before
jobInfo = acquireJob();
jobManager.execute(jobInfo); // JobInfo, not JobEntity

// after
JobEntity jobEntity = jobEntityManager.findById(jobInfo.getId());
jobManager.execute(jobEntity);
Defensive patterns

Strategy: type-guard

Validate before calling

if (!(job instanceof JobEntity)) { throw new IllegalArgumentException("execute() requires a JobEntity, got: " + job.getClass()); }
jobManager.execute((JobEntity) job);

Type guard

boolean isExecutableJob(Job job) { return job instanceof JobEntity; }

Try / catch

try { jobManager.execute(job); } catch (FlowableException e) { if (e.getMessage().startsWith("Only jobs with type JobEntity")) { JobEntity entity = jobEntityManager.findById(job.getId()); jobManager.execute(entity); } else throw e; }

Prevention

When it happens

Trigger: Calling DefaultJobManager.execute(Job) with a Job implementation that is not a JobEntity (e.g. a custom JobInfo implementation passed directly, or a custom async executor feeding non-entity jobs).

Common situations: Custom async executor implementations or test harnesses that wrap jobs in a JobInfo facade and pass the wrapper instead of the underlying JobEntity; upgrading Flowable versions where job interfaces changed.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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

Appendix: source

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

        return historyJobEntity;
    }

    @Override
    public void execute(JobInfo job) {
        if (job instanceof HistoryJobEntity) {
            callHistoryJobProcessors(HistoryJobProcessorContext.Phase.BEFORE_EXECUTE, (HistoryJobEntity) job);
            executeHistoryJob((HistoryJobEntity) job);
        } else if (job instanceof JobEntity) {
            callJobProcessors(JobProcessorContext.Phase.BEFORE_EXECUTE, (JobEntity) job);
            if (Job.JOB_TYPE_MESSAGE.equals(((Job) job).getJobType())) {
                executeMessageJob((JobEntity) job);
            } else if (Job.JOB_TYPE_TIMER.equals(((Job) job).getJobType())) {
                executeTimerJob((JobEntity) job);
            }

        } else {
            throw new FlowableException("Only jobs with type JobEntity are supported to be executed. It was " + job);
        }
    }

    @Override
    public void unacquire(JobInfo job) {

        if (job instanceof HistoryJob) {

            HistoryJobEntity jobEntity = jobServiceConfiguration.getHistoryJobEntityManager().findById(job.getId());
            if (jobEntity == null) {
                LOGGER.debug("History Job {} does not exist anymore and will not be unacquired. It has most likely been deleted "
                        + "e.g. as part of another concurrent part of a process / case instance.", job.getId());
                return;
            }

            jobEntity.setLockExpirationTime(null);
            jobEntity.setLockOwner(null);

View on GitHub (pinned to d6d39ce1c6)