flowable/flowable-engine · error · FlowableException

Use dedicated method for finding external worker jobs to…

Error message

Use dedicated method for finding external worker jobs to execute

What it means

MybatisExternalWorkerJobDataManager.findJobsToExecute unconditionally throws FlowableException stating that a dedicated method must be used instead. This overridden data-manager method is intentionally not supported for external worker jobs (acquisition uses category-based dedicated queries such as findJobsToExecuteAsync in the acquire command path), so calling the generic lookup is a programming error.

Solutions

  1. Use the dedicated external worker acquisition APIs (ExternalWorkerJobAcquireService / acquireJobsCmd) instead of the data manager's generic findJobsToExecute
  2. For regular async jobs, use the regular JobEntityManager, not the external worker one
  3. Filter categories via the dedicated acquisition query parameters rather than this method

Example fix

// before
List<ExternalWorkerJobEntity> jobs = externalWorkerJobDataManager.findJobsToExecute(categories, new Page(0, 10));
// after
List<ExternalWorkerJob> jobs = externalWorkerJobAcquireService.acquireExternalWorkerJobs(lockDuration, categories, null, 10);
Defensive patterns

Strategy: try-catch

Validate before calling

// Use the public service API, not the internal data manager:
// externalWorkerJobAcquireService.acquireExternalWorkerJobs(...)
// never call dataManager.findJobsToExecute for external worker jobs.

Try / catch

try {
    acquireViaGenericPath();
} catch (FlowableException e) {
    if (e.getMessage().contains("dedicated method")) {
        throw new IllegalStateException("Switch to ExternalWorkerJobAcquireService for external worker job acquisition", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling findJobsToExecute(enabledCategories, page) directly on MybatisExternalWorkerJobDataManager, or invoking an internal API/async executor path that resolves the external worker job entity manager's generic findJobsToExecute instead of the dedicated external-worker acquisition query.

Common situations: Custom async executor or acquisition code written for regular jobs being reused against external worker jobs; direct use of internal data managers in custom extensions; version changes where Flowable routed acquisition through a new dedicated method and old code still calls the generic one.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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

Appendix: source

Thrown at modules/flowable-job-service/src/main/java/org/flowable/job/service/impl/persistence/entity/data/impl/MybatisExternalWorkerJobDataManager.java:70

    @Override
    public Class<? extends ExternalWorkerJobEntity> getManagedEntityClass() {
        return ExternalWorkerJobEntityImpl.class;
    }

    @Override
    public ExternalWorkerJobEntity create() {
        return new ExternalWorkerJobEntityImpl();
    }

    @Override
    public ExternalWorkerJobEntity findJobByCorrelationId(String correlationId) {
        return getEntity("selectExternalWorkerJobByCorrelationId", correlationId, externalWorkerJobByCorrelationIdMatcher, true);
    }

    @Override
    public List<ExternalWorkerJobEntity> findJobsToExecute(List<String> enabledCategories, Page page) {
        throw new FlowableException("Use dedicated method for finding external worker jobs to execute");
    }

    @Override
    public List<ExternalWorkerJobEntity> findJobsByExecutionId(final String executionId) {
        DbSqlSession dbSqlSession = getDbSqlSession();

        // If the execution has been inserted in the same command execution as this query, there can't be any in the database 
        if (isEntityInserted(dbSqlSession, "execution", executionId)) {
            return getListFromCache(jobsByExecutionIdMatcher, executionId);
        }

        return getList(dbSqlSession, "selectExternalWorkerJobsByExecutionId", executionId, jobsByExecutionIdMatcher, true);
    }

    @Override
    @SuppressWarnings("unchecked")
    public List<ExternalWorkerJobEntity> findJobsByProcessInstanceId(final String processInstanceId) {
        return getDbSqlSession().selectList("selectExternalWorkerJobsByProcessInstanceId", processInstanceId);

View on GitHub (pinned to d6d39ce1c6)