flowable/flowable-engine · error · FlowableObjectNotFoundException
No External Worker job found for id:
Error message
No External Worker job found for id:
What it means
Thrown by resolveJob() as a FlowableObjectNotFoundException when findById(externalJobId) returns no ExternalWorkerJobEntity. The job id was well-formed but no matching external worker job row exists anymore — typically because the job was already completed, deleted, or the id is wrong or from another database/tenant.
Solutions
- Treat this as an expected race: catch FlowableObjectNotFoundException and skip/ack the job as already handled
- Verify the job id and database/tenant the command runs against match the acquisition
- Check job retention: if jobs complete too fast, ensure your worker fetches fresh ids rather than caching them
Example fix
// before
externalWorkerJobCompletionBuilder.externalJobId(jobId).workerId(workerId).complete();
// after
try {
externalWorkerJobCompletionBuilder.externalJobId(jobId).workerId(workerId).complete();
} catch (FlowableObjectNotFoundException e) {
LOGGER.info("External worker job {} already gone, skipping", jobId);
} Defensive patterns
Strategy: try-catch
Try / catch
try {
completionBuilder.externalJobId(jobId).workerId(workerId).complete();
} catch (FlowableObjectNotFoundException e) {
LOGGER.info("Job {} no longer exists (already completed/deleted), skipping", jobId);
} Prevention
- Do not cache external job ids across long processing delays
- Catch FlowableObjectNotFoundException around completion and treat it as idempotent success
- Ensure all workers point at the same database/tenant as the acquisition
When it happens
Trigger: Calling completeExternalWorkerJob/terminateExternalWorkerJob/failExternalWorkerJob with a job id that was deleted after completion; a stale job reference held across a long worker outage; typo'd or cross-environment job id; job removed by cleanup/history jobs in the meantime.
Common situations: Long-running worker holds an id while another worker instance completed the job; testing against one database and running against another; retry logic replaying completion for an already-processed job id.
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
- Active execution could not be found with activity id
- Batch part with id ' ' does not have a batch part document.
- Batch with id ' ' does not have a batch document.
- Cannot associate execution by id: no execution with id '
- Cannot find activity '" + activityId + "' in process…
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/23be49103a30bcf7.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable-job-service/src/main/java/org/flowable/job/service/impl/cmd/AbstractExternalWorkerJobCmd.java:70
}
protected abstract void runJobLogic(ExternalWorkerJobEntity externalWorkerJob, CommandContext commandContext);
protected ExternalWorkerJobEntity resolveJob() {
if (StringUtils.isEmpty(externalJobId)) {
throw new FlowableIllegalArgumentException("externalJobId must not be empty");
}
if (StringUtils.isEmpty(workerId)) {
throw new FlowableIllegalArgumentException("workerId must not be empty");
}
ExternalWorkerJobEntityManager externalWorkerJobEntityManager = jobServiceConfiguration.getExternalWorkerJobEntityManager();
ExternalWorkerJobEntity job = externalWorkerJobEntityManager.findById(externalJobId);
if (job == null) {
throw new FlowableObjectNotFoundException("No External Worker job found for id: " + externalJobId, ExternalWorkerJobEntity.class);
}
if (!Objects.equals(workerId, job.getLockOwner())) {
throw new FlowableIllegalArgumentException(workerId + " does not hold a lock on the requested job");
}
return job;
}
}
View on GitHub (pinned to d6d39ce1c6)