flowable/flowable-engine · error · FlowableException
task does not have an endTime, cannot delete
Error message
task does not have an endTime, cannot delete
What it means
DeleteHistoricTaskInstanceCmd requires the historic task to be finished: if getEndTime() is null the task is still open and the engine throws FlowableException. History deletion is reserved for completed tasks.
Source
Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/cmd/DeleteHistoricTaskInstanceCmd.java:53
public DeleteHistoricTaskInstanceCmd(String taskId) {
this.taskId = taskId;
}
@Override
public Object execute(CommandContext commandContext) {
if (taskId == null) {
throw new FlowableIllegalArgumentException("taskId is null");
}
// Check if task is completed
HistoricTaskInstanceEntity historicTaskInstance = CommandContextUtil.getHistoricTaskService().getHistoricTask(taskId);
if (historicTaskInstance == null) {
throw new FlowableObjectNotFoundException("No historic task instance found with id: " + taskId, HistoricTaskInstance.class);
}
if (historicTaskInstance.getEndTime() == null) {
throw new FlowableException("task does not have an endTime, cannot delete " + historicTaskInstance);
}
CommandContextUtil.getHistoryManager(commandContext).recordHistoricTaskDeleted(historicTaskInstance);
return null;
}
}
View on GitHub (pinned to d6d39ce1c6)
Solutions
- Complete the task first (taskService.complete(taskId)) before deleting its history
- Check historicTaskInstance.getEndTime() != null before deleting
- Use taskService-based deletion for active tasks instead of history APIs
Example fix
// before
historyService.deleteHistoricTask(taskId); // task not finished
// after
HistoricTaskInstance hti = historyService.createHistoricTaskInstanceQuery()
.taskId(taskId).singleResult();
if (hti != null && hti.getEndTime() != null) {
historyService.deleteHistoricTask(taskId);
} Defensive patterns
Strategy: validation
Validate before calling
HistoricTaskInstance hti = historyService.createHistoricTaskInstanceQuery()
.taskId(taskId).singleResult();
if (hti != null && hti.getEndTime() == null) {
throw new IllegalStateException("Task " + taskId + " is not finished; complete it first");
} Type guard
boolean isTaskFinished = id -> {
HistoricTaskInstance h = historyService.createHistoricTaskInstanceQuery()
.taskId(id).singleResult();
return h != null && h.getEndTime() != null;
}; Try / catch
try {
historyService.deleteHistoricTask(taskId);
} catch (FlowableException e) {
if (e.getMessage().contains("endTime")) {
logger.warn("Cannot delete history of unfinished task " + taskId);
}
} Prevention
- Only delete historic tasks whose endTime is set
- Complete or resolve active tasks before history cleanup
- Filter bulk deletions with .finished() on the historic task query
When it happens
Trigger: historyService.deleteHistoricTask(taskId) on an active/uncompleted task, or on a task whose history row exists but has not received an endTime (still claimed/in-progress).
Common situations: Cleanup jobs that skip the completion check, deleting a task's history while its process is still running, or confusion between taskService (runtime) and historyService (history) deletion APIs.
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 already deleted
- {task} is not suspended, so can't be activated
- Process instance is still running, cannot delete
- taskId is null
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/984a3a02ac872029.
Report an issue: GitHub.