flowable/flowable-engine · error · FlowableObjectNotFoundException

No External Worker job found for id: ${externalJobId}

Error message

No External Worker job found for id: ${externalJobId}

What it means

This FlowableObjectNotFoundException is thrown by AbstractExternalWorkerJobCmd.resolveJob when the external worker job entity manager cannot find an ExternalWorkerJobEntity with the given id. It means the engine has no persisted external worker job matching externalJobId, typically because it was already completed/deleted or the id is wrong. The lookup happens via CmmnEngineConfiguration's jobServiceConfiguration ExternalWorkerJobEntityManager.findById.

Source

Thrown at modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/cmd/AbstractExternalWorkerJobCmd.java:87

                .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");
        }

        CmmnEngineConfiguration cmmnEngineConfiguration = CommandContextUtil.getCmmnEngineConfiguration(commandContext);
        ExternalWorkerJobEntityManager externalWorkerJobEntityManager = cmmnEngineConfiguration.getJobServiceConfiguration().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)

Solutions

  1. Verify the externalJobId comes from the job payload returned by the acquire-jobs REST/Java API, not from a task or plan item id
  2. Re-fetch the job id immediately before the complete/terminate call and handle the not-found case as 'job already processed'
  3. Check for multiple workers competing for the same job; rely on lockOwner/lockExpirationTime instead of reusing old ids
  4. Confirm the app connects to the database where the job actually exists (correct datasource/engine configuration)
  5. Check case history/audit tables to see whether the case instance (and thus the job) was deleted

Example fix

// before
externalWorkerJobCompletionCmd = new ExternalWorkerJobCompleteCmd<>(externalJobId, workerId, variables);
managementService.executeCommand(externalWorkerJobCompletionCmd);
// after
ExternalWorkerJob job = cmmnTaskService.getExternalWorkerJob(externalJobId); // or query first
if (job != null && workerId.equals(job.getLockOwner())) {
    managementService.executeCommand(new ExternalWorkerJobCompleteCmd<>(externalJobId, workerId, variables));
} else {
    logger.info("External worker job {} no longer exists; skipping", externalJobId);
}
Defensive patterns

Strategy: validation

Validate before calling

ExternalWorkerJob job = cmmnTaskService.createExternalWorkerJobQueryBuilder().externalWorkerJobId(externalJobId).singleResult();
if (job == null) throw new IllegalStateException("External worker job " + externalJobId + " does not exist");

Try / catch

try { managementService.executeCommand(new ExternalWorkerJobCompleteCmd<>(jobId, workerId, vars)); }
catch (FlowableObjectNotFoundException e) { log.info("Job {} already processed/removed", jobId); }

Prevention

When it happens

Trigger: Calling an external worker command (acquire, complete, terminate, bpmn/cmmn external worker job commands) with an externalJobId that does not exist in ACT_RU_EXT_JOB / the CMMN runtime job tables; job already acquired and completed by another worker then terminated; job deleted by cascading case deletion before this command runs.

Common situations: Worker retries with a stale job id after the job completed; multiple workers racing on the same job; passing a task/plan-item id instead of the external job id; case instance removed between job fetch and command execution; wrong engine datasource pointing at a different database.

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


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