flowable/flowable-engine · error · FlowableException
{task} is already deleted
Error message
{task} is already deleted What it means
State check in ActivateTaskCmd.execute: the task was found, but its deletion flag shows it has already been deleted, so (re)activating it is not allowed. The task row still exists (historically) but is no longer a live runtime task.
Source
Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/cmd/ActivateTaskCmd.java:55
this.userId = userId;
}
@Override
public Void execute(CommandContext commandContext) {
ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext);
if (taskId == null) {
throw new FlowableIllegalArgumentException("taskId is null");
}
TaskEntity task = processEngineConfiguration.getTaskServiceConfiguration().getTaskService().getTask(taskId);
if (task == null) {
throw new FlowableObjectNotFoundException("Cannot find task with id " + taskId, Task.class);
}
if (task.isDeleted()) {
throw new FlowableException(task + " is already deleted");
}
if (!task.isSuspended()) {
throw new FlowableException(task + " is not suspended, so can't be activated");
}
Clock clock = processEngineConfiguration.getClock();
Date updateTime = clock.getCurrentTime();
task.setSuspendedTime(null);
task.setSuspendedBy(null);
if (task.getInProgressStartTime() != null) {
task.setState(Task.IN_PROGRESS);
} else if (task.getClaimTime() != null) {
task.setState(Task.CLAIMED);
} else {
task.setState(Task.CREATED);
}
task.setSuspensionState(SuspensionState.ACTIVE.getStateCode());View on GitHub (pinned to d6d39ce1c6)
Solutions
- Query the task again before activation and skip if it no longer exists or is deleted.
- Handle the exception gracefully for idempotent batch processing (treat 'already deleted' as success/skip).
- Remove stale taskId entries from your scheduling store when the process instance is deleted.
- Serialize suspension/activation operations for the same process instance to avoid races.
Example fix
// before
taskService.activateTask(taskId); // may hit deleted task
// after
Task t = taskService.createTaskQuery().taskId(taskId).singleResult();
if (t != null && !t.isDeleted() && t.isSuspended()) {
taskService.activateTask(taskId);
} Defensive patterns
Strategy: try-catch
Validate before calling
Task t = taskService.createTaskQuery().taskId(taskId).singleResult();
if (t == null || t.isDeleted() || !t.isSuspended()) {
return;
} Try / catch
try {
taskService.activateTask(taskId);
} catch (FlowableException e) {
if (e.getMessage() != null && e.getMessage().contains("already deleted")) {
// treat as no-op in idempotent batch
} else {
throw e;
}
} Prevention
- Make suspend/activate batch jobs idempotent.
- Subscribe to entity-deleted events to purge pending task operations.
- Avoid racing deletion and activation on the same instance.
When it happens
Trigger: taskService.activateTask(id) on a task whose process instance was deleted or whose task was removed concurrently between lookup and activation.
Common situations: Race condition where another thread/user deletes the process instance while a batch job suspends/activates tasks; retry logic re-running activation on already-deleted tasks.
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
- Task ${taskId} is already deleted
- {task} is not suspended, so can't be activated
- task does not have an endTime, cannot delete
- Cannot resume task with id '
- No task associated. Call businessProcess.startTask() first.
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/bb289f4fda14a221.
Report an issue: GitHub.