flowable/flowable-engine · error · FlowableIllegalArgumentException

taskId or processInstanceId is required

Error message

taskId or processInstanceId is required

What it means

GetHistoricIdentityLinksForTaskCmd accepts either a taskId or a processInstanceId, but throws FlowableIllegalArgumentException when both are null. It retrieves historic identity links (users/groups involved) for a task or a whole process instance, so at least one selector is required.

Solutions

  1. Ensure at least one of taskId or processInstanceId is non-null before constructing the command.
  2. Add a pre-check that raises a clear caller-side error naming the missing selector.
  3. If neither id is available, use a different history query (e.g. query historic identity links by type/user) instead of this command.

Example fix

// before
historyService.findHistoricIdentityLinksForTask(taskId, processInstanceId);
// after
if (taskId == null && processInstanceId == null) {
    throw new IllegalArgumentException("Provide taskId or processInstanceId to query historic identity links");
}
historyService.findHistoricIdentityLinksForTask(taskId, processInstanceId);
Defensive patterns

Strategy: validation

Validate before calling

if (taskId == null && processInstanceId == null) {
    throw new IllegalArgumentException("Either taskId or processInstanceId must be provided");
}

Try / catch

try {
    return historyService.findHistoricIdentityLinksForTask(taskId, processInstanceId);
} catch (FlowableIllegalArgumentException e) {
    return Collections.emptyList();
}

Prevention

When it happens

Trigger: new GetHistoricIdentityLinksForTaskCmd(null, null), typically when optional taskId/processInstanceId parameters both resolve to null at the call site, or when routing logic fails to populate either argument.

Common situations: Building a generic history viewer where the caller passes whichever id it has and both are absent; copy-pasted calls to the identity-link API without filling in parameters; tasks/instances already removed from history so the caller's ids were nulled out.

Understand the failure class

Background: "mutually exclusive" flag errors: what "can't supply both nx and xx", "--raw is not compatible with -i" and "cannot be used with" mean, and how to fix them — this error's family across 29 libraries.

Related errors


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

Appendix: source

Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/cmd/GetHistoricIdentityLinksForTaskCmd.java:42

import org.flowable.identitylink.api.IdentityLinkType;
import org.flowable.identitylink.api.history.HistoricIdentityLink;
import org.flowable.identitylink.service.HistoricIdentityLinkService;
import org.flowable.identitylink.service.impl.persistence.entity.HistoricIdentityLinkEntity;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.service.impl.persistence.entity.HistoricTaskInstanceEntity;

/**
 * @author Frederik Heremans
 */
public class GetHistoricIdentityLinksForTaskCmd implements Command<List<HistoricIdentityLink>>, Serializable {

    private static final long serialVersionUID = 1L;
    protected String taskId;
    protected String processInstanceId;

    public GetHistoricIdentityLinksForTaskCmd(String taskId, String processInstanceId) {
        if (taskId == null && processInstanceId == null) {
            throw new FlowableIllegalArgumentException("taskId or processInstanceId is required");
        }
        this.taskId = taskId;
        this.processInstanceId = processInstanceId;
    }

    @Override
    public List<HistoricIdentityLink> execute(CommandContext commandContext) {
        if (taskId != null) {
            return getLinksForTask(commandContext);
        } else {
            return getLinksForProcessInstance(commandContext);
        }
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    protected List<HistoricIdentityLink> getLinksForTask(CommandContext commandContext) {
        ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext);
        HistoricTaskInstanceEntity task = processEngineConfiguration.getTaskServiceConfiguration().getHistoricTaskService().getHistoricTask(taskId);

View on GitHub (pinned to d6d39ce1c6)