{"record":{"id":"c2eaebccd92eae84","repo":"theonedev/onedev","slug":"group-groupname-is-included-multiple-times","errorCode":null,"errorMessage":"Group '${groupName}' is included multiple times","messagePattern":"Group '(.+?)' is included multiple times","errorType":"validation","errorClass":"ExplicitException","httpStatus":null,"severity":"error","filePath":"server-core/src/main/java/io/onedev/server/util/reviewrequirement/ReviewRequirement.java","lineNumber":77,"sourceCode":"\t\t\t\t\t\tif (!users.contains(user)) \n\t\t\t\t\t\t\tusers.add(user);\n\t\t\t\t\t\telse \n\t\t\t\t\t\t\tthrow new ExplicitException(\"User '\" + userName + \"' is included multiple times\");\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthrow new ExplicitException(\"Unable to find user '\" + userName + \"'\");\n\t\t\t\t\t}\n\t\t\t\t} else if (criteria.groupCriteria() != null) {\n\t\t\t\t\tString groupName = getValue(criteria.groupCriteria().Value());\n\t\t\t\t\tGroup group = OneDev.getInstance(GroupService.class).find(groupName);\n\t\t\t\t\tif (group != null) {\n\t\t\t\t\t\tif (!groups.containsKey(group)) {\n\t\t\t\t\t\t\tTerminalNode digit = criteria.groupCriteria().DIGIT();\n\t\t\t\t\t\t\tif (digit != null) \n\t\t\t\t\t\t\t\tgroups.put(group, Integer.parseInt(digit.getText()));\n\t\t\t\t\t\t\telse \n\t\t\t\t\t\t\t\tgroups.put(group, 1);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tthrow new ExplicitException(\"Group '\" + groupName + \"' is included multiple times\");\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthrow new ExplicitException(\"Unable to find group '\" + groupName + \"'\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\t\t\t\n\t\t}\n\t\t\n\t\treturn new ReviewRequirement(users, groups);\n\t}\n\n\tprivate static String getValue(TerminalNode terminal) {\n\t\treturn StringUtils.unescape(FenceAware.unfence(terminal.getText())).trim();\n\t}\n\t\n\tpublic List<User> getUsers() {\n\t\treturn users;\n\t}","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/theonedev/onedev/blob/d44925c47c37992c828ea673a5f9620539bc3ff2/server-core/src/main/java/io/onedev/server/util/reviewrequirement/ReviewRequirement.java#L59-L95","documentation":"While parsing a review requirement, each 'group:<name>' criterion is resolved and stored in a map keyed by group; if the same group appears twice ExplicitException is thrown because the required approver count for that group would be ambiguous.","triggerScenarios":"Parsing a requirement string with the same group in multiple criteria, e.g. 'group:qa or group:qa', including duplicates arising from combined and/or branches.","commonSituations":"Requirements assembled by merging branch policies; template-generated strings that append clauses without checking existing groups.","solutions":["Deduplicate the group in the requirement string","Merge duplicate group clauses into one clause with the required count, e.g. 'group:qa with 2 approvals'","Catch ExplicitException on save and validate requirement strings programmatically"],"exampleFix":"// before\nreviewRequirement = \"group:qa or group:qa\";\n// after\nreviewRequirement = \"group:qa\";","handlingStrategy":"validation","validationCode":"java.util.Set<String> seen = new java.util.HashSet<>();\nfor (String name : extractGroupNames(requirement)) {\n    if (!seen.add(name))\n        throw new ExplicitException(\"Duplicate group in requirement: \" + name);\n}","typeGuard":null,"tryCatchPattern":"try {\n    ReviewRequirement.parse(req);\n} catch (ExplicitException e) {\n    ui.showError(e.getMessage());\n}","preventionTips":["Merge duplicate group clauses into one with an explicit count","Generate requirement strings from a Set of group names","Validate expressions at config-save time"],"tags":["review-requirement","duplicate","validation"],"backgroundTag":"invalid-argument-value","analyzedSha":"d44925c47c37992c828ea673a5f9620539bc3ff2","analyzedAt":"2026-09-06T07:18:27.995Z","contentChangedAt":"2026-09-06T07:18:27.995Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}