{"record":{"id":"f194a97aaf91454b","repo":"flowable/flowable-engine","slug":"invalid-query-usage-cannot-set-both-candidategrou-f194a9","errorCode":null,"errorMessage":"Invalid query usage: cannot set both candidateGroup and candidateUser","messagePattern":"Invalid query usage: cannot set both candidateGroup and candidateUser","errorType":"validation","errorClass":"ActivitiIllegalArgumentException","httpStatus":null,"severity":"error","filePath":"modules/flowable5-engine/src/main/java/org/activiti/engine/impl/TaskQueryImpl.java","lineNumber":535,"sourceCode":"        if (candidateGroups != null) {\n            throw new ActivitiIllegalArgumentException(\"Invalid query usage: cannot set both candidateGroup and candidateGroupIn\");\n        }\n\n        if (orActive) {\n            currentOrQueryObject.candidateGroup = candidateGroup;\n        } else {\n            this.candidateGroup = candidateGroup;\n        }\n        return this;\n    }\n\n    @Override\n    public TaskQuery taskCandidateOrAssigned(String userIdForCandidateAndAssignee) {\n        if (candidateGroup != null) {\n            throw new ActivitiIllegalArgumentException(\"Invalid query usage: cannot set candidateGroup\");\n        }\n        if (candidateUser != null) {\n            throw new ActivitiIllegalArgumentException(\"Invalid query usage: cannot set both candidateGroup and candidateUser\");\n        }\n\n        if (orActive) {\n            currentOrQueryObject.bothCandidateAndAssigned = true;\n            currentOrQueryObject.userIdForCandidateAndAssignee = userIdForCandidateAndAssignee;\n        } else {\n            this.bothCandidateAndAssigned = true;\n            this.userIdForCandidateAndAssignee = userIdForCandidateAndAssignee;\n        }\n\n        return this;\n    }\n\n    @Override\n    public TaskQuery taskCandidateGroupIn(List<String> candidateGroups) {\n        if (candidateGroups == null) {\n            throw new ActivitiIllegalArgumentException(\"Candidate group list is null\");\n        }","sourceCodeStart":517,"sourceCodeEnd":553,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable5-engine/src/main/java/org/activiti/engine/impl/TaskQueryImpl.java#L517-L553","documentation":"taskCandidateOrAssigned(String userIdForCandidateAndAssignee) throws this error when candidateUser has already been set via taskCandidateUser(...). taskCandidateOrAssigned already includes candidate-user semantics, so combining it with taskCandidateUser would duplicate/conflict the identity-link conditions. Use one or the other.","triggerScenarios":"Calling taskQuery.taskCandidateOrAssigned(userId) after taskQuery.taskCandidateUser(someUser) on the same query (or within the same OR block) - checked at TaskQueryImpl.java:535.","commonSituations":"Query composition layers that add a candidate-user filter while a newer code path switched to candidate-or-assigned; refactoring a task list to show claimed+unclaimed tasks but leaving the old candidateUser call; copy-pasted OR query blocks.","solutions":["Remove the taskCandidateUser call and rely on taskCandidateOrAssigned alone","If both semantics are truly needed, issue separate queries and union the results in application code","Add a builder/validation layer that asserts only one candidate-filter mode is configured"],"exampleFix":"// before\nquery.taskCandidateUser(\"kermit\");\nquery.taskCandidateOrAssigned(\"kermit\"); // throws\n// after\nquery.taskCandidateOrAssigned(\"kermit\");","handlingStrategy":"validation","validationCode":"if (candidateUser != null && candidateOrAssignedUser != null) {\n    throw new IllegalStateException(\"taskCandidateOrAssigned already covers candidate-user semantics\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    query.taskCandidateOrAssigned(userId);\n} catch (ActivitiIllegalArgumentException e) {\n    log.warn(\"candidateUser already set: {}\", e.getMessage());\n    query = taskService.createTaskQuery().taskCandidateUser(candidateUser);\n}","preventionTips":["Prefer taskCandidateOrAssigned when you want claimed + unclaimed tasks for a user","Remove legacy taskCandidateUser calls when migrating to candidate-or-assigned","Assert single candidate-filter mode in a query-builder wrapper"],"tags":["task-query","mutually-exclusive","candidate-user","activiti"],"backgroundTag":"mutually-exclusive-options","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-18T11:17:12.947Z"}