{"record":{"id":"6272dd9d647b3c30","repo":"weaviate/weaviate","slug":"w-roles-already-exist","errorCode":null,"errorMessage":"%w: roles already exist","messagePattern":"%w: roles already exist","errorType":"exception","errorClass":"ErrBadRequest","httpStatus":400,"severity":"error","filePath":"cluster/rbac/manager.go","lineNumber":179,"sourceCode":"\t}\n\n\treq := &cmd.CreateRolesRequest{}\n\tif err := json.Unmarshal(c.SubCommand, req); err != nil {\n\t\treturn fmt.Errorf(\"%w: %w\", ErrBadRequest, err)\n\t}\n\n\t// Scan all roles, not just the exact names, to enforce short-name\n\t// uniqueness across namespaces. The handler's pre-check read is not atomic\n\t// with this write; applies run serially, so this is the authoritative guard.\n\tif req.RoleCreation {\n\t\tallRoles, err := m.authZ.GetRoles()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\texisting := maps.Keys(allRoles)\n\t\tfor name := range req.Roles {\n\t\t\tif namespacing.FindShortNameConflict(existing, name) != namespacing.NoRoleConflict {\n\t\t\t\treturn fmt.Errorf(\"%w: roles already exist\", ErrBadRequest)\n\t\t\t}\n\t\t}\n\t}\n\n\tif req.Version < cmd.RBACLatestCommandPolicyVersion {\n\t\tfor roleName, policies := range req.Roles {\n\t\t\tpermissions := []*authorization.Policy{}\n\t\t\tfor _, p := range policies {\n\t\t\t\tpermissions = append(permissions, &p)\n\t\t\t}\n\t\t\t// remove old permissions\n\t\t\tif err := m.authZ.RemovePermissions(roleName, permissions); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\n\treqMigrated, err := migrateUpsertRolesPermissions(req)","sourceCodeStart":161,"sourceCodeEnd":197,"githubUrl":"https://github.com/weaviate/weaviate/blob/75aa4b6d11f8818305aafd4440b4e32794f7ca04/cluster/rbac/manager.go#L161-L197","documentation":"The RAFT-applied RBAC create/upsert command was rejected because one of the requested role names conflicts with an existing role. The Manager scans ALL existing roles — not just exact names — via namespacing.FindShortNameConflict, so a short name that collides across namespaces also triggers this. This is the authoritative guard at apply time because the handler's earlier pre-check read is not atomic with the write.","triggerScenarios":"Calling the create-roles RBAC API (applied as UpsertRolesPermissions with req.RoleCreation=true) when the role name already exists, or when its namespace-qualified short name conflicts with another existing role. Also hit by concurrent or retried create attempts after a first successful apply.","commonSituations":"Retry logic re-sending a role-creation request that already succeeded; two clients creating the same role concurrently; role names differing only by namespace prefix so their short names collide; migrations that re-create existing roles.","solutions":["Check whether the role already exists (GET roles) before creating, and skip or turn the create into an update of permissions","Pick a role name whose short name is unique across all namespaces (namespacing enforces short-name uniqueness, not just exact match)","Make create calls idempotent: on this error treat the role as existing and continue instead of retrying the create","If a partial multi-role request failed, remove the conflicting role names and resubmit only the new ones"],"exampleFix":"// before\nclient.Roles().Creator().WithRole(&schema.Role{Name: \"viewer\", Permissions: perms}).Do(ctx) // fails: roles already exist\n// after\nexisting, _ := client.Roles().AllGetter().Do(ctx)\nif _, ok := existing[\"viewer\"]; !ok {\n    client.Roles().Creator().WithRole(&schema.Role{Name: \"viewer\", Permissions: perms}).Do(ctx)\n}","handlingStrategy":"validation","validationCode":"roles, err := client.Roles().AllGetter().Do(ctx)\nif err != nil { return err }\nif _, exists := roles[\"viewer\"]; exists {\n    // skip create or update permissions instead\n}","typeGuard":"func roleExists(roles map[string]schema.Role, name string) bool {\n    _, ok := roles[name]\n    return ok\n}","tryCatchPattern":"if err := client.Roles().Creator().WithRole(r).Do(ctx); err != nil && strings.Contains(err.Error(), \"roles already exist\") {\n    // treat as existing, continue\n}","preventionTips":["List existing roles before any create and make creation idempotent","Keep role short names globally unique, avoiding namespace-prefix collisions","Serialize role-management changes through one operator/process to avoid races"],"tags":["rbac","raft","conflict","bad-request"],"backgroundTag":"role-already-exists","analyzedSha":"75aa4b6d11f8818305aafd4440b4e32794f7ca04","analyzedAt":"2026-09-04T14:58:20.392Z","contentChangedAt":"2026-09-04T14:58:20.392Z","schemaVersion":2},"datasetVersion":"2026-09-11T21:17:09.523Z"}