flowable/flowable-engine · warning · FlowableException
Cannot delete
Error message
Cannot delete
What it means
After finding the job, DeleteJobCmd rejects deletion when the job is locked (lockOwner != null), i.e. the async executor's acquisition thread already picked it up and is executing it. Flowable throws a plain FlowableException("Cannot delete ... when the job is being executed. Try again later.") to prevent deleting a job mid-execution.
Source
Thrown at modules/flowable-job-service/src/main/java/org/flowable/job/service/impl/cmd/DeleteJobCmd.java:92
protected JobEntity getJobToDelete(CommandContext commandContext) {
if (jobId == null) {
throw new FlowableIllegalArgumentException("jobId is null");
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Deleting job {}", jobId);
}
JobEntity job = jobServiceConfiguration.getJobEntityManager().findById(jobId);
if (job == null) {
throw new FlowableObjectNotFoundException("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 FlowableException("Cannot delete " + job + " when the job is being executed. Try again later.");
}
return job;
}
}
View on GitHub (pinned to d6d39ce1c6)
Solutions
- Retry after a short delay — the message explicitly says 'Try again later'; the lock expires if the executor dies or the job finishes.
- Stop or scale down the async executor (or pause job acquisition) before bulk job deletion/maintenance.
- Check job.getLockOwner()/lock expiration time before deleting, and skip locked jobs.
- Shut down the engine gracefully (executor shutdown waits for running jobs) in tests/scripts that delete jobs.
Example fix
// before
managementService.deleteJob(jobId); // may be locked by the async executor
// after
Job job = managementService.createJobQuery().jobId(jobId).singleResult();
if (job != null && job.getLockOwner() == null) {
managementService.deleteJob(jobId);
} Defensive patterns
Strategy: retry
Validate before calling
Job job = managementService.createJobQuery().jobId(jobId).singleResult();
if (job != null && job.getLockOwner() != null) {
LOGGER.info("Job {} is locked by {} — retry later", jobId, job.getLockOwner());
return;
} Try / catch
int attempts = 0;
while (attempts < 3) {
try {
managementService.deleteJob(jobId);
break;
} catch (FlowableException e) {
if (!e.getMessage().contains("being executed") || ++attempts == 3) throw e;
Thread.sleep(2000);
}
} Prevention
- Check lockOwner before deleting jobs in clustered deployments.
- Pause or shut down the async executor for maintenance deletions.
- Back off and retry — locks expire when acquisition lapses.
When it happens
Trigger: Calling managementService.deleteJob / DeleteJobCmd on a job that a job executor node has acquired (ACT_RU_JOB.LOCK_OWNER_ set, lock expiry not yet passed) — typical in clustered setups or immediately after an async step became runnable.
Common situations: Deleting an async continuation job while a service task is executing on another node; cleanup scripts running while the async executor is active; long-running jobs whose lock is repeatedly renewed so retries keep colliding.
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
- there must be at least one job entity manager
- Cannot delete ${job} when the job is being executed. Try aga
- Optimistic locking exception (using global acquire lock) for
- exception for engine {} during async job acquisition: {}
- Error while waiting for global acquire lock for engine {}
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/85b694d5074fc93f.
Report an issue: GitHub.