flowable/flowable-engine · error · FlowableObjectNotFoundException
No External Worker job found for id
Error message
No External Worker job found for id: {externalJobId} What it means
After loading the job by id, resolveJob throws FlowableObjectNotFoundException (with ExternalWorkerJobEntity.class as the entity type) when the job row does not exist. The command cannot proceed because the referenced external worker job is gone or was never created with that id.
Solutions
- Catch FlowableObjectNotFoundException in the worker and treat the job as already handled (skip/retry acquisition).
- Re-acquire fresh jobs instead of retrying stale job ids from a local cache.
- Verify the id and that the command runs against the same engine/database/tenant where the job was acquired.
Example fix
// before
jobService.complete(jobId, workerId, variables); // throws if job gone
// after
try {
jobService.complete(jobId, workerId, variables);
} catch (FlowableObjectNotFoundException e) {
log.info("Job {} already gone, skipping", jobId);
} Defensive patterns
Strategy: try-catch
Validate before calling
ExternalWorkerJob job = jobService.createExternalWorkerJobQuery().externalJobId(jobId).singleResult(); // check before acting
Try / catch
try {
jobService.complete(jobId, workerId, variables);
} catch (FlowableObjectNotFoundException e) {
// job already completed/expired: skip and re-acquire
} Prevention
- Treat not-found as an expected race in idempotent workers
- Re-acquire jobs instead of retrying stale ids
- Keep job references short-lived (within the lock duration)
When it happens
Trigger: Calling complete/fail/release with an externalJobId that no longer exists: job already completed and deleted, expired/failed job removed by job cleaner, id typo, or querying against a different database/tenant.
Common situations: Two workers raced and one finished the job first; long retries after the job expired (lock expired and job deleted); stale job id cached on the worker side; cross-environment id reuse.
Understand the failure class
Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.
Related errors
- Could not find job for id
- No external worker job found with 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/e9b7e950288e70b9.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/cmd/AbstractExternalWorkerJobCmd.java:87
processEngineConfiguration.getIdentityLinkServiceConfiguration().getIdentityLinkService()
.deleteIdentityLinksByScopeIdAndType(externalWorkerJob.getCorrelationId(), ScopeTypes.EXTERNAL_WORKER);
}
protected ExternalWorkerJobEntity resolveJob(CommandContext commandContext) {
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)