flowable/flowable-engine · error · ActivitiIllegalArgumentException
Candidate group list is empty
Error message
Candidate group list is empty
What it means
taskCandidateGroupIn(List<String>) rejects an empty list: an IN filter with zero values has no meaning, so the engine throws ActivitiIllegalArgumentException instead of producing a query that matches nothing or everything ambiguously.
Solutions
- Check !candidateGroups.isEmpty() and skip the filter call when empty (result: no candidate-group filter).
- If an empty selection should return no results by design, short-circuit and skip executing the query instead.
- Ensure upstream group resolvers return null/absent rather than an empty list when no filter is intended, and guard accordingly.
Example fix
// before
List<String> groups = userPermissions.getGroups(); // may be empty
query.taskCandidateGroupIn(groups); // throws when empty
// after
List<String> groups = userPermissions.getGroups();
if (groups != null && !groups.isEmpty()) {
query.taskCandidateGroupIn(groups);
} Defensive patterns
Strategy: validation
Validate before calling
// Java
if (candidateGroups != null && !candidateGroups.isEmpty()) {
query.taskCandidateGroupIn(candidateGroups);
} // else: no group filter (or short-circuit for zero results by design) Try / catch
try {
query.taskCandidateGroupIn(groups);
} catch (org.activiti.engine.ActivitiIllegalArgumentException e) {
// decide: skip filter, or return empty result set intentionally
} Prevention
- Check list size before every *In(...) query filter call.
- Decide explicitly whether an empty selection means 'no filter' or 'no results'.
- Test query builders with empty collections.
When it happens
Trigger: Calling taskCandidateGroupIn(Collections.emptyList()) or any list of size 0 on a HistoricTaskInstanceQuery (also inside an or() block).
Common situations: Multi-select UI where no group was chosen but the query is still built; group filter derived from a permission set that is empty for the current user; JDBC/IN-clause habits carried over where empty lists were filtered out downstream.
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
- callbackIds is null or empty
- Candidate group is null
- Candidate group list is null
- Candidate user is null
- Could not find a deployment with id
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/d213746145929e0e.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable5-engine/src/main/java/org/activiti/engine/impl/HistoricTaskInstanceQueryImpl.java:1103
throw new ActivitiIllegalArgumentException("Invalid query usage: cannot set both candidateGroup and candidateGroupIn");
}
if (inOrStatement) {
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;View on GitHub (pinned to d6d39ce1c6)