flowable/flowable-engine · error · FlowableObjectNotFoundException
No external worker job found with id '
Error message
No external worker job found with id '
What it means
DeleteExternalWorkerJobCmd loads the external worker job via ExternalWorkerJobEntityManager.findById and throws FlowableObjectNotFoundException when no entity matches the given id. The referenced row does not exist in the external worker job table at deletion time.
Solutions
- Look up the job first via the external worker job query (managementService.getExternalWorkerJobs or ExternalWorkerJobQueryBuilder) and only delete when found.
- Treat FlowableObjectNotFoundException as success (idempotent delete) in code that runs cancellation concurrently.
- Confirm the id is an external worker job id, not a timer/message/suspended job id — different tables hold different job kinds.
- Verify both the worker and the engine point at the same database/schema and tenant.
Example fix
// before
managementService.executeCommand(new DeleteExternalWorkerJobCmd(externalJobId));
// after
if (managementService.createExternalWorkerJobQueryBuilder().externalWorkerJobId(externalJobId).count() > 0) {
managementService.executeCommand(new DeleteExternalWorkerJobCmd(externalJobId));
} Defensive patterns
Strategy: try-catch
Validate before calling
long count = managementService.createExternalWorkerJobQueryBuilder()
.externalWorkerJobId(externalJobId).count();
if (count == 0) { LOGGER.info("External worker job {} not found; skipping", externalJobId); return; } Try / catch
try {
managementService.executeCommand(new DeleteExternalWorkerJobCmd(externalJobId));
} catch (FlowableObjectNotFoundException e) {
LOGGER.info("External worker job {} already removed", externalJobId);
} Prevention
- Account for concurrent workers completing/acquiring the same job.
- Verify the id is an external worker job id, not another job kind.
- Use idempotent delete logic in cancellation paths.
When it happens
Trigger: Deleting an external worker job whose id was already consumed (e.g. after acquireAndLock/complete/bpmnError removed it), an id belonging to another job type (regular timer/message job instead of external worker job), or a fabricated/typo id.
Common situations: External worker job completed concurrently by another worker or lock expired and the job was recycled; cancel paths in tests running twice; querying ACT_RU_EXT_JOB ids through the wrong management API; multi-engine setups where the id exists in a different engine's database.
Understand the failure class
Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.
Related errors
- Could not find job for id
- No External Worker job found for id
- Active execution could not be found with activity id
- at least one of userId or groups must be provided
- Batch part with id ' ' does not have a batch part document.
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/8695e29a5ed29abc.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable-job-service/src/main/java/org/flowable/job/service/impl/cmd/DeleteExternalWorkerJobCmd.java:50
protected String jobId;
protected JobServiceConfiguration jobServiceConfiguration;
public DeleteExternalWorkerJobCmd(String jobId, JobServiceConfiguration jobServiceConfiguration) {
this.jobId = jobId;
this.jobServiceConfiguration = jobServiceConfiguration;
}
@Override
public Void execute(CommandContext commandContext) {
if (jobId == null) {
throw new FlowableIllegalArgumentException("jobId is null");
}
ExternalWorkerJobEntityManager jobEntityManager = jobServiceConfiguration.getExternalWorkerJobEntityManager();
ExternalWorkerJobEntity job = jobEntityManager.findById(jobId);
if (job == null) {
throw new FlowableObjectNotFoundException("No external worker job found with id '" + jobId + "'", Job.class);
}
FlowableEventDispatcher eventDispatcher = jobServiceConfiguration.getEventDispatcher();
if (eventDispatcher != null && eventDispatcher.isEnabled()) {
eventDispatcher.dispatchEvent(FlowableJobEventBuilder.createEntityEvent(FlowableEngineEventType.JOB_CANCELED, job),
jobServiceConfiguration.getEngineName());
}
jobEntityManager.delete(job);
return null;
}
}
View on GitHub (pinned to d6d39ce1c6)