flowable/flowable-engine · error · ActivitiIllegalArgumentException

Invalid query usage: cannot set both candidateGroupIn and…

Error message

Invalid query usage: cannot set both candidateGroupIn and candidateGroup

What it means

HistoricTaskInstanceQueryImpl.candidateGroupIn() throws ActivitiIllegalArgumentException when candidateGroup was already set on the query. The two filters are mutually exclusive: candidateGroup matches one group, candidateGroupIn matches a list. The query builder refuses to combine them because the persisted query would be ambiguous.

Solutions

  1. Remove the taskCandidateGroup(...) call and keep only taskCandidateGroupIn(...) (or vice versa)
  2. If you need the single group plus others, add it to the list passed to taskCandidateGroupIn
  3. If both filters are genuinely needed, build two separate queries or use or() scopes appropriately

Example fix

// before
query.taskCandidateGroup("hr");
query.taskCandidateGroupIn(Arrays.asList("sales", "support"));
// after
query.taskCandidateGroupIn(Arrays.asList("hr", "sales", "support"));
Defensive patterns

Strategy: validation

Validate before calling

if (candidateGroup != null && candidateGroups != null) {
    throw new IllegalArgumentException("Use either candidateGroup or candidateGroupIn, not both");
}

Try / catch

try {
    query.taskCandidateGroupIn(groups);
} catch (ActivitiIllegalArgumentException e) {
    if (e.getMessage().contains("cannot set both")) { /* drop one filter and rebuild */ }
}

Prevention

When it happens

Trigger: Calling query.taskCandidateGroup("hr") earlier in the fluent chain and later calling query.taskCandidateGroupIn(Arrays.asList("hr","sales")) (or the reverse via internal ordering) on the same HistoricTaskInstanceQuery.

Common situations: Building queries programmatically where filter criteria come from user input or config, so both the single-group and group-list variants get applied; refactoring code from candidateGroup to candidateGroupIn while leaving the old call in place.

Related errors


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

Appendix: source

Thrown at modules/flowable5-engine/src/main/java/org/activiti/engine/impl/HistoricTaskInstanceQueryImpl.java:1107

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

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

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

        if (candidateGroup != null) {
            throw new ActivitiIllegalArgumentException("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 (inOrStatement) {
            this.currentOrQueryObject.involvedUser = involvedUser;
        } else {
            this.involvedUser = involvedUser;
        }
        return this;

View on GitHub (pinned to d6d39ce1c6)