flowable/flowable-engine · error · FlowableIllegalArgumentException

Candidate group list is empty

Error message

Candidate group list is empty

What it means

TaskQueryImpl.taskCandidateGroupIn() throws this when the collection is non-null but empty. An empty IN list is meaningless or invalid SQL, so Flowable rejects it explicitly. Treat 'no filter' as not calling the setter, not as an empty list.

Solutions

  1. Call taskCandidateGroupIn() only when the collection has at least one element.
  2. On empty, either skip the filter or short-circuit to an empty result set if that matches your authorization model.
  3. Fix group resolution so expected groups are actually populated.
  4. Do not use empty collections to mean 'match nothing'; handle that case in caller logic.

Example fix

// before
TaskQuery query = taskService.createTaskQuery().taskCandidateGroupIn(groups);
// after
if (groups == null || groups.isEmpty()) {
    return Collections.emptyList();
}
TaskQuery query = taskService.createTaskQuery().taskCandidateGroupIn(groups);
Defensive patterns

Strategy: validation

Validate before calling

if (candidateGroups == null || candidateGroups.isEmpty()) {
    throw new IllegalArgumentException("candidateGroups must contain at least one group");
}
query.taskCandidateGroupIn(candidateGroups);

Type guard

boolean isNonEmptyCandidateGroups(Collection<String> g) { return g != null && !g.isEmpty(); }

Try / catch

try {
    query.taskCandidateGroupIn(groups);
} catch (FlowableIllegalArgumentException e) {
    logger.warn("Empty candidate group list: {}", e.getMessage());
    return Collections.emptyList(); // no groups -> no candidate tasks
}

Prevention

When it happens

Trigger: Calling taskQuery.taskCandidateGroupIn(new ArrayList<>()) or a list reduced to zero elements by prior filtering/authorization logic.

Common situations: User belongs to no groups so the resolved group list is empty; authorization filter stripped all groups; default empty list passed unconditionally in generic query-builder code.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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

Appendix: source

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

        if (orActive) {
            currentOrQueryObject.bothCandidateAndAssigned = true;
            currentOrQueryObject.userIdForCandidateAndAssignee = userIdForCandidateAndAssignee;
        } else {
            this.bothCandidateAndAssigned = true;
            this.userIdForCandidateAndAssignee = userIdForCandidateAndAssignee;
        }

        return this;
    }

    @Override
    public TaskQuery 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 (orActive) {
            currentOrQueryObject.candidateGroups = candidateGroups;
        } else {
            this.candidateGroups = candidateGroups;
        }
        return this;
    }

    @Override
    public TaskQuery ignoreAssigneeValue() {
        if (orActive) {
            currentOrQueryObject.ignoreAssigneeValue = true;

View on GitHub (pinned to d6d39ce1c6)