flowable/flowable-engine · error · FlowableIllegalArgumentException

taskId is null

Error message

taskId is null

What it means

DeleteHistoricTaskInstanceCmd validates that taskId is non-null before looking up the historic task. A null taskId is a caller-side contract violation and the engine throws FlowableIllegalArgumentException without touching the database.

Source

Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/cmd/DeleteHistoricTaskInstanceCmd.java:43

import org.flowable.task.service.impl.persistence.entity.HistoricTaskInstanceEntity;

/**
 * @author Tom Baeyens
 */
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.getHistoryManager(commandContext).recordHistoricTaskDeleted(historicTaskInstance);
        
        return null;
    }

}

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Pass a valid non-null task id to the delete API
  2. Capture Task.getId() when the task completes and store it for later deletion
  3. Null-check the id before invoking the command

Example fix

// before
historyService.deleteHistoricTask(taskId); // taskId null
// after
if (taskId != null) {
    historyService.deleteHistoricTask(taskId);
}
Defensive patterns

Strategy: validation

Validate before calling

if (taskId == null || taskId.trim().isEmpty()) {
    throw new IllegalArgumentException("taskId must be provided before deleting historic task");
}

Type guard

boolean hasTaskId = id -> id != null && !id.trim().isEmpty();

Try / catch

try {
    historyService.deleteHistoricTask(taskId);
} catch (FlowableIllegalArgumentException e) {
    logger.warn("Null taskId passed to deleteHistoricTask", e);
}

Prevention

When it happens

Trigger: historyService.deleteHistoricTask(taskId) or related APIs executed with a null id, e.g. a task id variable never set, or a completed task listener forwarding a null id.

Common situations: Using task.getId() from an object that was never persisted, mapping errors in delegation code, or calling delete inside an event handler where the task reference is not yet available.

Related errors


AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11). Data as JSON: /api/errors/176ee14c5f3a6e01. Report an issue: GitHub.