flowable/flowable-engine · error · ActivitiIllegalArgumentException
Candidate group list is empty
Error message
Candidate group list is empty
What it means
TaskQueryImpl.taskCandidateGroupIn(List<String>) throws ActivitiIllegalArgumentException when the candidateGroups list is empty. An empty IN list would generate invalid or meaningless SQL (IN ()), so the engine rejects it at query-build time. Callers must pass at least one candidate group.
Solutions
- Check list.isEmpty() before calling taskCandidateGroupIn and handle the no-groups case explicitly (e.g. skip the candidate-group filter or return an empty result by policy).
- Combine the null and empty checks into one guard helper reused by all query builders.
- Catch ActivitiIllegalArgumentException around query construction and report that no candidate groups were supplied.
Example fix
// before
taskQuery.taskCandidateGroupIn(userGroups); // may be empty
// after
if (userGroups != null && !userGroups.isEmpty()) {
taskQuery.taskCandidateGroupIn(userGroups);
} else {
return Collections.emptyList(); // policy: no groups => no candidate tasks
} Defensive patterns
Strategy: validation
Validate before calling
if (candidateGroups == null || candidateGroups.isEmpty()) {
return Collections.emptyList(); // or skip the filter
}
taskQuery.taskCandidateGroupIn(candidateGroups); Type guard
boolean isNonEmpty(List<String> list) { return list != null && !list.isEmpty(); } Try / catch
try {
return taskQuery.taskCandidateGroupIn(groups).list();
} catch (ActivitiIllegalArgumentException e) {
log.warn("Empty candidate group filter rejected: {}", e.getMessage());
return Collections.emptyList();
} Prevention
- Decide explicit policy for 'no groups' (empty result vs unfiltered query)
- Guard both null and empty in the same check
- Log which filter was rejected to speed up debugging
When it happens
Trigger: Calling taskQuery.taskCandidateGroupIn(new ArrayList<>()) or passing a collection that was filtered down to zero elements before the query was built.
Common situations: A user belongs to no groups, group filtering removed all entries, or a CSV/config value split into zero tokens; the query is then built unconditionally.
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
- Candidate group list is null
- None of the given process instance ids can be null
- Process category list is empty
- Process category list is null
- Process instance id list is empty
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/ff6554ef7d5de657.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable5-engine/src/main/java/org/activiti/engine/impl/TaskQueryImpl.java:556
if (orActive) {
currentOrQueryObject.bothCandidateAndAssigned = true;
currentOrQueryObject.userIdForCandidateAndAssignee = userIdForCandidateAndAssignee;
} else {
this.bothCandidateAndAssigned = true;
this.userIdForCandidateAndAssignee = userIdForCandidateAndAssignee;
}
return this;
}
@Override
public TaskQuery 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 (orActive) {
currentOrQueryObject.candidateGroups = candidateGroups;
} else {
this.candidateGroups = candidateGroups;
}
return this;
}
@Override
public TaskQuery taskTenantId(String tenantId) {
if (tenantId == null) {
throw new ActivitiIllegalArgumentException("task tenant id is null");View on GitHub (pinned to d6d39ce1c6)