flowable/flowable-engine · error · FlowableIllegalArgumentException
taskId is null
Error message
taskId is null
What it means
DeleteHistoricTaskInstanceCmd validates taskId before fetching the historic task via the HistoricTaskService. A null taskId throws FlowableIllegalArgumentException("taskId is null"). It can surface indirectly from activity behaviors (e.g. executeActivityBehavior) that delete historic tasks with an unpopulated id.
Source
Thrown at modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/cmd/DeleteHistoricTaskInstanceCmd.java:44
/**
* @author Tijs Rademakers
*/
public class DeleteHistoricTaskInstanceCmd implements Command<Object>, Serializable {
private static final long serialVersionUID = 1L;
protected String taskId;
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.getCmmnHistoryManager(commandContext).recordHistoricTaskDeleted(historicTaskInstance);
return null;
}
}View on GitHub (pinned to d6d39ce1c6)
Solutions
- Null/empty-check taskId before calling deleteHistoricTaskInstance
- Ensure the source entity (Task/HistoricTaskInstance) actually has a persisted id before deleting
- Log and skip null-id tasks in batch/listener code rather than throwing
Example fix
// before
historyService.deleteHistoricTaskInstance(task.getId());
// after
if (task != null && task.getId() != null) {
historyService.deleteHistoricTaskInstance(task.getId());
} Defensive patterns
Strategy: validation
Validate before calling
if (taskId == null || taskId.isEmpty()) { return; }
historyService.deleteHistoricTaskInstance(taskId); Type guard
boolean hasId(Task t) { return t != null && t.getId() != null && !t.getId().isEmpty(); } Try / catch
try { historyService.deleteHistoricTaskInstance(taskId); }
catch (FlowableIllegalArgumentException e) { if ("taskId is null".equals(e.getMessage())) { log.warn("skipped historic task delete: null id"); } else throw e; } Prevention
- Guard entity ids before deleting historic tasks
- In listeners, skip tasks that were never persisted
- Validate DTO mapping keeps the id field populated
When it happens
Trigger: Calling CmmnHistoryService.deleteHistoricTaskInstance(null) or delegating commands with a task entity whose id is null (unsaved task, mapping failure).
Common situations: Processing a task result from an empty query without null check; DTO mapping dropping the id field; cleanup listeners firing for tasks never persisted.
Related errors
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/5a14f7121e1bf135.
Report an issue: GitHub.