flowable/flowable-engine · error · FlowableException

Could not find job for id

Error message

Could not find job for id ${jobId}

What it means

UnacquireExternalWorkerJobCmd.execute throws this FlowableException (not a typed not-found exception) when findById on the external worker job entity manager returns null for the given jobId. The job to unacquire no longer exists in the runtime tables, so the release operation cannot be performed.

Solutions

  1. Treat this as a benign race in the handler: catch FlowableException and log/skip if the job is gone
  2. Refresh the job id from a fresh ExternalWorkerJobQuery before unacquiring
  3. Reduce lock expiration races by handling jobs well within the lock duration
  4. Verify the datasource points at the same database the job was acquired from

Example fix

// before
externalWorkerJobService.unacquireExternalWorkerJob(jobId, workerId); // throws if job vanished
// after
try {
    externalWorkerJobService.unacquireExternalWorkerJob(jobId, workerId);
} catch (FlowableException e) {
    if (e.getMessage() != null && e.getMessage().contains("Could not find job")) {
        logger.info("Job {} already gone; skipping unacquire", jobId);
    } else {
        throw e;
    }
}
Defensive patterns

Strategy: try-catch

Validate before calling

ExternalWorkerJobEntity job = externalWorkerJobService.createExternalWorkerJobQuery().jobId(jobId).singleResult();
if (job == null) { /* job gone, skip */ }

Try / catch

try {
    externalWorkerJobService.unacquireExternalWorkerJob(jobId, workerId);
} catch (FlowableException e) {
    logger.info("Job {} disappeared before unacquire (already completed?)", jobId);
}

Prevention

When it happens

Trigger: Calling unacquireExternalWorkerJob with an id of a job that was completed, deleted, or never an external worker job; the job lock expired and another worker completed/removed it; stale id retained after the async executor processed the job; wrong datasource/environment.

Common situations: Two workers racing on the same expired lock; job ids cached across restarts; pointing the client at a different database (dev vs prod); job moved out of the external worker table after acquisition.

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/f0bcb0d70eb6ab00. Report an issue: GitHub.

Appendix: source

Thrown at modules/flowable-job-service/src/main/java/org/flowable/job/service/impl/cmd/UnacquireExternalWorkerJobCmd.java:50

        this.workerId = workerId;
        this.jobServiceConfiguration = jobServiceConfiguration;
    }

    @Override
    public Void execute(CommandContext commandContext) {
        if (StringUtils.isEmpty(jobId)) {
            throw new FlowableIllegalArgumentException("job id must not be empty");
        }

        if (StringUtils.isEmpty(workerId)) {
            throw new FlowableIllegalArgumentException("worker id must not be empty");
        }

        ExternalWorkerJobEntityManager externalWorkerJobEntityManager = jobServiceConfiguration.getExternalWorkerJobEntityManager();

        ExternalWorkerJobEntity jobEntity = externalWorkerJobEntityManager.findById(jobId);
        if (jobEntity == null) {
            throw new FlowableException("Could not find job for id " + jobId);
        }
        
        if (!jobEntity.getLockOwner().equals(workerId)) {
            throw new FlowableException(jobEntity + " is locked with a different worker id");
        }

        jobEntity.setLockExpirationTime(null);
        jobEntity.setLockOwner(null);
        externalWorkerJobEntityManager.update(jobEntity);
        if (jobEntity.isExclusive()) {
            new UnlockExclusiveJobCmd(jobEntity, jobServiceConfiguration).execute(commandContext);
        }
        
        return null;
    }
}

View on GitHub (pinned to d6d39ce1c6)