flowable/flowable-engine · error · FlowableIllegalArgumentException

Invalid query usage: cannot set both candidateGroupIn and…

Error message

Invalid query usage: cannot set both candidateGroupIn and candidateGroup

What it means

taskCandidateGroupIn(Collection<String>) throws this error when the query already has candidateGroup (from taskCandidateGroup) set. candidateGroup and candidateGroupIn are mutually exclusive filters; only one representation can be applied to the generated SQL.

Solutions

  1. Remove either the taskCandidateGroup(...) or the taskCandidateGroupIn(...) call on the same query object.
  2. Use taskCandidateGroupIn(Collections.singletonList(group)) for everything so only one API is ever used.
  3. Build a new query instance when the filter strategy changes.

Example fix

// before
query.taskCandidateGroup("backoffice");
query.taskCandidateGroupIn(groupList); // throws

// after
List<String> effective = new ArrayList<>(groupList);
effective.add("backoffice");
query.taskCandidateGroupIn(effective);
Defensive patterns

Strategy: validation

Validate before calling

// merge both sources into one list and use only taskCandidateGroupIn
List<String> allGroups = new ArrayList<>();
if (candidateGroup != null) { allGroups.add(candidateGroup); }
if (groupList != null) { allGroups.addAll(groupList); }
if (!allGroups.isEmpty()) {
    query.taskCandidateGroupIn(allGroups);
}

Type guard

boolean canSetCandidateGroupIn(HistoricTaskInstanceQueryImpl q) {
    return q.candidateGroup == null; // single group not yet set
}

Try / catch

try {
    query.taskCandidateGroupIn(groups);
} catch (FlowableIllegalArgumentException e) {
    throw new IllegalStateException(
        "Query already filters by candidateGroup; remove one of the two group filters", e);
}

Prevention

When it happens

Trigger: Chaining .taskCandidateGroup("backoffice").taskCandidateGroupIn(groups) on the same HistoricTaskInstanceQuery instance.

Common situations: Accumulating filters from multiple sources (a default single-group filter plus a user-selected group list); generic query-building code that sets whichever group criteria it has without clearing the other.

Related errors


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

Appendix: source

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

            this.currentOrQueryObject.candidateGroup = candidateGroup;
        } else {
            this.candidateGroup = candidateGroup;
        }
        return this;
    }

    @Override
    public HistoricTaskInstanceQuery taskCandidateGroupIn(Collection<String> candidateGroups) {
        if (candidateGroups == null) {
            throw new FlowableIllegalArgumentException("Candidate group list is null");
        }

        if (candidateGroups.isEmpty()) {
            throw new FlowableIllegalArgumentException("Candidate group list is empty");
        }

        if (candidateGroup != null) {
            throw new FlowableIllegalArgumentException("Invalid query usage: cannot set both candidateGroupIn and candidateGroup");
        }

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

    @Override
    public HistoricTaskInstanceQuery taskInvolvedUser(String involvedUser) {
        if (involvedUser == null) {
            throw new FlowableIllegalArgumentException("involved user is null");
        }

        if (inOrStatement) {
            this.currentOrQueryObject.involvedUser = involvedUser;

View on GitHub (pinned to d6d39ce1c6)