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
- Use the dedicated external worker acquisition APIs (ExternalWorkerJobAcquireService / acquireJobsCmd) instead of the data manager's generic findJobsToExecute
- For regular async jobs, use the regular JobEntityManager, not the external worker one
- 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
- Use ExternalWorkerJobAcquireService for external worker acquisition, never the data manager directly
- Do not reuse regular-job executor internals for external worker jobs
- When extending Flowable internals, check which dedicated methods the data manager exposes
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
- can't clear configuration beans
- can't search values in configuration beans
- Cannot change fixed value with
- Cannot move a history job to an executable job
- CommandInvoker must be the last interceptor in the chain
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)