flowable/flowable-engine · error · FlowableIllegalArgumentException

Invalid query usage: cannot set both candidateGroup and…

Error message

Invalid query usage: cannot set both candidateGroup and candidateGroupIn

What it means

TaskQueryImpl.taskCandidateGroup() throws this when taskCandidateGroupIn() has already been called (candidateGroups != null). The two setters represent mutually exclusive filtering strategies (single group vs list), and combining them would produce an ambiguous query. Flowable rejects the conflicting call at build time.

Solutions

  1. Use either taskCandidateGroup() or taskCandidateGroupIn(), never both on the same query.
  2. Build a fresh query object instead of reusing a cached TaskQuery instance.
  3. Wrap the single group in a list and call taskCandidateGroupIn(Collections.singletonList(group)) when the source may vary.
  4. Refactor query composition logic so only one candidate filter branch executes.

Example fix

// before
query.taskCandidateGroupIn(groups);
query.taskCandidateGroup("management");
// after
if (groups != null && !groups.isEmpty()) {
    query.taskCandidateGroupIn(groups);
} else {
    query.taskCandidateGroup("management");
}
Defensive patterns

Strategy: validation

Validate before calling

if (useSingleGroup) {
    query.taskCandidateGroup(group);
} else if (groups != null && !groups.isEmpty()) {
    query.taskCandidateGroupIn(groups);
}

Try / catch

try {
    query.taskCandidateGroup(group);
} catch (FlowableIllegalArgumentException e) {
    logger.warn("Conflicting candidate filters: {}", e.getMessage());
    query = taskService.createTaskQuery(); // rebuild with only one filter
}

Prevention

When it happens

Trigger: Calling taskCandidateGroup("g1") after taskCandidateGroupIn(list), or building one query object incrementally/reused where both setters got invoked (including inside or() branches that share state checks).

Common situations: Query builder reused across requests retaining earlier state; code merged from two features each setting a different candidate filter; dynamic query composition adding both filters unconditionally.

Related errors


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

Appendix: source

Thrown at modules/flowable-task-service/src/main/java/org/flowable/task/service/impl/TaskQueryImpl.java:626

        if (involvedGroups.isEmpty()) {
            throw new FlowableIllegalArgumentException("Involved groups are empty");
        }
        if (orActive) {
            currentOrQueryObject.involvedGroups = involvedGroups;
        } else {
            this.involvedGroups = involvedGroups;
        }
        return this;
    }

    @Override
    public TaskQueryImpl taskCandidateGroup(String candidateGroup) {
        if (candidateGroup == null) {
            throw new FlowableIllegalArgumentException("Candidate group is null");
        }

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

        if (orActive) {
            currentOrQueryObject.candidateGroup = candidateGroup;
        } else {
            this.candidateGroup = candidateGroup;
        }
        return this;
    }

    @Override
    public TaskQuery taskCandidateOrAssigned(String userIdForCandidateAndAssignee) {
        if (candidateGroup != null) {
            throw new FlowableIllegalArgumentException("Invalid query usage: cannot set candidateGroup");
        }
        if (candidateUser != null) {
            throw new FlowableIllegalArgumentException("Invalid query usage: cannot set both candidateGroup and candidateUser");
        }

View on GitHub (pinned to d6d39ce1c6)