flowable/flowable-engine · warning · ActivitiException

Cannot delete job when the job is being executed. Try again…

Error message

Cannot delete job when the job is being executed. Try again later.

What it means

DeleteJobCmd refuses to delete a job whose lockOwner is set, meaning the async job executor's acquisition thread has already acquired the job and it is executing (or about to execute). Deleting it mid-execution could corrupt processing, so the command throws ActivitiException telling the caller to retry later. This is a deliberate concurrency guard, not a bug.

Solutions

  1. Wait briefly and retry the deletion after the job finishes (poll until lockOwner is null)
  2. Stop/suspend the job executor (ProcessEngineConfiguration asyncExecutorActive=false) before deleting
  3. Clear the lock manually by updating ACT_RU_JOB to null out LOCK_OWNER_/LOCK_EXP_TIME_ during maintenance downtime, then delete
  4. Catch ActivitiException and implement retry with backoff

Example fix

// before
managementService.deleteJob(jobId);
// after
Job job = managementService.createJobQuery().jobId(jobId).singleResult();
if (job != null && job.getLockOwner() == null) {
    managementService.deleteJob(jobId);
} else {
    // retry later or suspend the job executor first
}
Defensive patterns

Strategy: retry

Validate before calling

Job job = managementService.createJobQuery().jobId(jobId).singleResult();
boolean locked = job != null && job.getLockOwner() != null;

Type guard

boolean deletable(Job job) { return job != null && job.getLockOwner() == null; }

Try / catch

try {
    managementService.deleteJob(jobId);
} catch (ActivitiException e) {
    if (e.getMessage().contains("being executed")) { /* schedule retry with backoff */ }
}

Prevention

When it happens

Trigger: Calling ManagementService.deleteJob(jobId) (or similar delete APIs routed through DeleteJobCmd) while the async job executor has acquired the job — i.e. JOB_.LOCK_OWNER_ and LOCK_EXP_TIME_ are set and unexpired.

Common situations: Admin canceling a stuck-looking job while the job executor is actively running it; clustered setups where another node's acquisition thread grabbed the job; deleting a job immediately after triggering an async continuation.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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

Appendix: source

Thrown at modules/flowable5-engine/src/main/java/org/activiti/engine/impl/cmd/DeleteJobCmd.java:68

    protected JobEntity getJobToDelete(CommandContext commandContext) {
        if (jobId == null) {
            throw new ActivitiIllegalArgumentException("jobId is null");
        }
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Deleting job {}", jobId);
        }

        JobEntity job = commandContext.getJobEntityManager().findJobById(jobId);
        if (job == null) {
            throw new ActivitiObjectNotFoundException("No job found with id '" + jobId + "'", Job.class);
        }

        // We need to check if the job was locked, ie acquired by the job acquisition thread
        // This happens if the job was already acquired, but not yet executed.
        // In that case, we can't allow to delete the job.
        if (job.getLockOwner() != null) {
            throw new ActivitiException("Cannot delete job when the job is being executed. Try again later.");
        }
        return job;
    }

}

View on GitHub (pinned to d6d39ce1c6)