flowable/flowable-engine · error · FlowableIllegalArgumentException

Invalid query usage: cannot set both taskNameIn and taskName

Error message

Invalid query usage: cannot set both taskNameIn and taskName

What it means

HistoricTaskInstanceQueryImpl.taskNameIn(Collection) throws FlowableIllegalArgumentException when a taskName filter was already set on the same query. Flowable forbids combining exact-name equality (taskName) with the name IN-list filter because the SQL conditions are mutually exclusive in the intended semantics. Only one of the two name filters may be applied per query.

Solutions

  1. Choose one filter: remove the taskName call when using taskNameIn, or vice versa.
  2. Convert the single name into a one-element list and use only taskNameIn: taskNameIn(List.of(name)).
  3. Track which name filter is active in your builder code and set it exactly once.

Example fix

// before
query.taskName("review");
query.taskNameIn(Arrays.asList("review", "approve")); // conflict
// after
query.taskNameIn(Arrays.asList("review", "approve"));
Defensive patterns

Strategy: validation

Validate before calling

boolean exactNameSet = (query instanceof HistoricTaskInstanceQueryImpl q) && q.getTaskName() != null;
if (exactNameSet && names != null) {
    throw new IllegalStateException("Use either taskName or taskNameIn, not both");
}

Type guard

boolean nameFiltersCompatible(String taskName, String taskNameLike, Collection<String> taskNameIn) {
    int set = (taskName != null ? 1 : 0) + (taskNameLike != null ? 1 : 0) + (taskNameIn != null && !taskNameIn.isEmpty() ? 1 : 0);
    return set <= 1;
}

Try / catch

try {
    query.taskNameIn(names);
} catch (FlowableIllegalArgumentException e) {
    logger.warn("Conflicting task name filters: {}", e.getMessage());
}

Prevention

When it happens

Trigger: Calling historicTaskInstanceQuery().taskName("review").taskNameIn(List.of("review", "approve")) — i.e., invoking taskNameIn after taskName (or vice versa) on the same query instance, including via shared query-building code.

Common situations: Layered filter builders where a base method sets taskName and a later override adds taskNameIn; merging user-supplied filters where both exact and list filters were provided; copy-pasted query code reusing a partially built query object.

Related errors


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

Appendix: source

Thrown at modules/flowable-task-service/src/main/java/org/flowable/task/service/impl/HistoricTaskInstanceQueryImpl.java:746

        if (inOrStatement) {
            this.currentOrQueryObject.taskName = taskName;
        } else {
            this.taskName = taskName;
        }
        return this;
    }

    @Override
    public HistoricTaskInstanceQuery taskNameIn(Collection<String> taskNameList) {
        if (taskNameList == null) {
            throw new FlowableIllegalArgumentException("Task name list is null");
        }
        if (taskNameList.isEmpty()) {
            throw new FlowableIllegalArgumentException("Task name list is empty");
        }

        if (taskName != null) {
            throw new FlowableIllegalArgumentException("Invalid query usage: cannot set both taskNameIn and taskName");
        }
        if (taskNameLike != null) {
            throw new FlowableIllegalArgumentException("Invalid query usage: cannot set both taskNameIn and taskNameLike");
        }
        if (taskNameLikeIgnoreCase != null) {
            throw new FlowableIllegalArgumentException("Invalid query usage: cannot set both taskNameIn and taskNameLikeIgnoreCase");
        }

        if (inOrStatement) {
            currentOrQueryObject.taskNameList = taskNameList;
        } else {
            this.taskNameList = taskNameList;
        }
        return this;
    }

    @Override
    public HistoricTaskInstanceQuery taskNameInIgnoreCase(Collection<String> taskNameList) {

View on GitHub (pinned to d6d39ce1c6)