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
- Remove the taskCandidateGroup(...) call and keep only taskCandidateGroupIn(...) (or vice versa)
- If you need the single group plus others, add it to the list passed to taskCandidateGroupIn
- 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
- Decide once per query whether the filter is single-group or multi-group
- Centralize task-candidate filter building in one helper
- Add a unit test asserting only one candidate filter is applied
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
- Cannot combine onlyTimers() with onlyMessages() in the same…
- Cannot combine onlyTimers() with onlyMessages() in the same…
- Cannot combine subScopeId(String) with withoutSubScopeId()…
- Cannot combine subScopeIds with withoutSubScopeId in the…
- Cannot combine withoutSubScopeId() with subScopeId(String)…
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)