{"record":{"id":"f32f0b3fb08c0578","repo":"chenhg5/cc-connect","slug":"config-s-users-user-q-appears-in-both-role-q","errorCode":null,"errorMessage":"config: %s.users: user %q appears in both role %q and %q","messagePattern":"config: (.+?)\\.users: user %q appears in both role %q and %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"config/config.go","lineNumber":1182,"sourceCode":"\t\treturn nil\n\t}\n\tif len(u.Roles) == 0 {\n\t\treturn fmt.Errorf(\"config: %s.users has no roles defined\", prefix)\n\t}\n\twildcardCount := 0\n\tseenUserIDs := make(map[string]string) // userID → role name\n\tfor roleName, rc := range u.Roles {\n\t\tif len(rc.UserIDs) == 0 {\n\t\t\treturn fmt.Errorf(\"config: %s.users.roles.%s has empty user_ids\", prefix, roleName)\n\t\t}\n\t\tfor _, uid := range rc.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(\"config: %s.users: user %q appears in both role %q and %q\", prefix, uid, prev, roleName)\n\t\t\t}\n\t\t\tseenUserIDs[lower] = roleName\n\t\t}\n\t}\n\tif wildcardCount > 1 {\n\t\treturn fmt.Errorf(\"config: %s.users: wildcard user_ids=[\\\"*\\\"] appears in multiple roles\", prefix)\n\t}\n\tif u.DefaultRole != \"\" {\n\t\tif _, ok := u.Roles[u.DefaultRole]; !ok {\n\t\t\treturn fmt.Errorf(\"config: %s.users.default_role %q does not match any defined role\", prefix, u.DefaultRole)\n\t\t}\n\t}\n\treturn nil\n}\n\n// SaveActiveProvider persists the active provider name for a project.\n// It uses surgical text editing to preserve comments and unknown fields.\nfunc SaveActiveProvider(projectName, providerName string) error {","sourceCodeStart":1164,"sourceCodeEnd":1200,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/config/config.go#L1164-L1200","documentation":"This validation error is returned while loading/validating the users section of a project's role-based access config. Each user_id may only be assigned to one role; duplicates (compared case-insensitively) are ambiguous so the config is rejected. It prevents silently inconsistent permission resolution where two roles would both claim the same user.","triggerScenarios":"Calling config load/validate (via the engine startup or LoadConfig) when a project's [[projects.users]] block lists the same user_id string (case-insensitive) under two different roles in u.Roles.","commonSituations":"Hand-editing config.toml and copy-pasting a user into a second role; a colleague already granted the user admin while you added them as member; case differences ('Alice' vs 'alice') hiding the duplicate.","solutions":["Find the role names in the message and remove the duplicate user_id from one of the two roles","Keep one authoritative role per user; use default_role instead of listing the user in multiple roles","Search config.toml for the user id case-insensitively to catch case-variant duplicates","Reload/restart so validation re-runs and confirms the config is accepted"],"exampleFix":"// before\n[[projects.users.roles.admin]]\nuser_ids = [\"alice\"]\n[[projects.users.roles.member]]\nuser_ids = [\"alice\"]\n// after\n[[projects.users.roles.admin]]\nuser_ids = [\"alice\"]\n[[projects.users.roles.member]]\nuser_ids = [\"bob\"]","handlingStrategy":"validation","validationCode":"func validateNoDupUsers(roles map[string][]string) error {\n\tseen := map[string]string{}\n\tfor role, ids := range roles {\n\t\tfor _, id := range ids {\n\t\t\tk := strings.ToLower(id)\n\t\t\tif prev, ok := seen[k]; ok {\n\t\t\t\treturn fmt.Errorf(\"user %q in %q and %q\", id, prev, role)\n\t\t\t}\n\t\t\tseen[k] = role\n\t\t}\n\t}\n\treturn nil\n}","typeGuard":null,"tryCatchPattern":"if err := cfg.Validate(); err != nil {\n\tif strings.Contains(err.Error(), \"appears in both role\") {\n\t\t// fix config before proceeding\n\t}\n\treturn err\n}","preventionTips":["Keep a single source of truth for role assignments; generate roles from one user->role map","Always compare user ids case-insensitively when editing config","Lint config.toml in CI before deploying role changes"],"tags":["config","validation","duplicate-user"],"backgroundTag":"invalid-config-value","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"}