{"record":{"id":"f229c70fb0b287c2","repo":"chenhg5/cc-connect","slug":"user-q-appears-in-both-role-q-and-q","errorCode":null,"errorMessage":"user %q appears in both role %q and %q","messagePattern":"user %q appears in both role %q and %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/user_roles.go","lineNumber":223,"sourceCode":"\tif len(roles) == 0 {\n\t\treturn fmt.Errorf(\"no roles defined\")\n\t}\n\twildcardCount := 0\n\tseenUserIDs := make(map[string]string) // userID → role name\n\troleNames := make(map[string]bool, len(roles))\n\tfor _, ri := range roles {\n\t\troleNames[ri.Name] = true\n\t\tif len(ri.UserIDs) == 0 {\n\t\t\treturn fmt.Errorf(\"role %q has empty user_ids\", ri.Name)\n\t\t}\n\t\tfor _, uid := range ri.UserIDs {\n\t\t\tif uid == \"*\" {\n\t\t\t\twildcardCount++\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tlower := strings.ToLower(uid)\n\t\t\tif prev, dup := seenUserIDs[lower]; dup {\n\t\t\t\treturn fmt.Errorf(\"user %q appears in both role %q and %q\", uid, prev, ri.Name)\n\t\t\t}\n\t\t\tseenUserIDs[lower] = ri.Name\n\t\t}\n\t}\n\tif wildcardCount > 1 {\n\t\treturn fmt.Errorf(\"wildcard user_ids=[\\\"*\\\"] appears in multiple roles\")\n\t}\n\tif defaultRole != \"\" {\n\t\tif !roleNames[defaultRole] {\n\t\t\treturn fmt.Errorf(\"default_role %q does not match any defined role\", defaultRole)\n\t\t}\n\t}\n\treturn nil\n}\n\n// Stop terminates all per-role rate limiter goroutines. Nil-receiver safe.\nfunc (m *UserRoleManager) Stop() {\n\tif m == nil {","sourceCodeStart":205,"sourceCodeEnd":241,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/core/user_roles.go#L205-L241","documentation":"ValidateRoleInputs enforces that each concrete user ID appears in at most one role. It lowercases user IDs for comparison (case-insensitive) and reports both the offending user and the two conflicting role names when a duplicate is found. This prevents ambiguous role resolution.","triggerScenarios":"Calling ValidateRoleInputs where two roles both list the same user ID (in any letter casing, e.g. \"Alice\" in role A and \"alice\" in role B). Wildcard \"*\" entries bypass this check.","commonSituations":"Merging two project-user configs that each assigned the same user; copy-pasting a user into a new role without removing them from the old one; a team member added to both 'admin' and 'developer'.","solutions":["Remove the user from one of the two roles named in the error","Unify user ID casing across roles to make the intent explicit (matching is case-insensitive either way)","Pre-process the payload in the caller to detect cross-role duplicates and surface a friendlier message"],"exampleFix":"// before\nroles := []core.RoleInput{\n    {Name: \"admin\", UserIDs: []string{\"Alice\"}},\n    {Name: \"dev\", UserIDs: []string{\"alice\"}},\n}\n// after\nroles := []core.RoleInput{\n    {Name: \"admin\", UserIDs: []string{\"alice\"}},\n    {Name: \"dev\", UserIDs: []string{\"bob\"}},\n}","handlingStrategy":"validation","validationCode":"seen := map[string]string{}\nfor _, r := range roles {\n    for _, u := range r.UserIDs {\n        k := strings.ToLower(u)\n        if _, dup := seen[k]; dup {\n            return fmt.Errorf(\"duplicate user %q\", u)\n        }\n        seen[k] = r.Name\n    }\n}\ncore.ValidateRoleInputs(defaultRole, roles)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Treat user IDs case-insensitively everywhere","Deduplicate users across roles before saving configs","Avoid adding a user to a new role without removing them from the old one"],"tags":["validation","roles","duplicate"],"backgroundTag":"conflicting-config-options","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}