flowable/flowable-engine · error · FlowableIllegalArgumentException
Invalid query usage: cannot set candidateGroup
Error message
Invalid query usage: cannot set candidateGroup
What it means
TaskQueryImpl.taskCandidateOrAssigned() throws this when candidateGroup was already set on the query. candidateOrAssigned filters by candidate user and/or assignee, which is incompatible with a candidate-group filter on the same query. The conflict is detected eagerly at build time.
Solutions
- Remove one of the conflicting setters; keep either candidateGroup or candidateOrAssigned.
- Create a new query instance per request to avoid leftover state.
- If both group and user filtering are needed, run two queries or use or() semantics deliberately with only compatible criteria.
- Restructure the builder so the group filter branch excludes the candidateOrAssigned branch.
Example fix
// before
query.taskCandidateGroup("management");
query.taskCandidateOrAssigned(userId);
// after
TaskQuery query = taskService.createTaskQuery();
if (useGroupFilter) {
query.taskCandidateGroup("management");
} else {
query.taskCandidateOrAssigned(userId);
} Defensive patterns
Strategy: validation
Validate before calling
if (useGroupFilter) {
query.taskCandidateGroup(group);
} else {
query.taskCandidateOrAssigned(userId);
} Try / catch
try {
query.taskCandidateOrAssigned(userId);
} catch (FlowableIllegalArgumentException e) {
logger.warn("candidateGroup already set: {}", e.getMessage());
query = taskService.createTaskQuery();
} Prevention
- Set only one candidate-related filter per query (candidateGroup, candidateUser, candidateGroupIn, candidateOrAssigned)
- Rebuild queries instead of mutating reused ones
- Document filter exclusivity in shared query-builder utilities
- Add regression tests for combined-filter scenarios
When it happens
Trigger: Calling taskQuery.taskCandidateOrAssigned(userId) after taskCandidateGroup("g"), including state left from a reused query object or an or() branch where candidateGroup was set first.
Common situations: Combining query-building code paths for group-based and user-based task assignment; reusing a TaskQuery across iterations; conditional logic that sets candidateGroup for some inputs then falls through to candidateOrAssigned.
Related errors
- Invalid query usage: cannot set both candidateGroup and…
- Invalid query usage: cannot set both candidateGroupIn and…
- Candidate group is null
- Candidate group list is empty
- Candidate group list is null
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/2a2ee3f005f311a0.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable-task-service/src/main/java/org/flowable/task/service/impl/TaskQueryImpl.java:640
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");
}
if (orActive) {
currentOrQueryObject.bothCandidateAndAssigned = true;
currentOrQueryObject.userIdForCandidateAndAssignee = userIdForCandidateAndAssignee;
} else {
this.bothCandidateAndAssigned = true;
this.userIdForCandidateAndAssignee = userIdForCandidateAndAssignee;
}
return this;
}
@Override
public TaskQuery taskCandidateGroupIn(Collection<String> candidateGroups) {View on GitHub (pinned to d6d39ce1c6)