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

  1. Null/empty-check taskId before calling deleteHistoricTaskInstance
  2. Ensure the source entity (Task/HistoricTaskInstance) actually has a persisted id before deleting
  3. 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

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.