weaviate/weaviate · error · ErrBadRequest

%w: roles already exist

Error message

%w: roles already exist

What it means

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.

Source

Thrown at cluster/rbac/manager.go:179

	}

	req := &cmd.CreateRolesRequest{}
	if err := json.Unmarshal(c.SubCommand, req); err != nil {
		return fmt.Errorf("%w: %w", ErrBadRequest, err)
	}

	// Scan all roles, not just the exact names, to enforce short-name
	// uniqueness across namespaces. The handler's pre-check read is not atomic
	// with this write; applies run serially, so this is the authoritative guard.
	if req.RoleCreation {
		allRoles, err := m.authZ.GetRoles()
		if err != nil {
			return err
		}
		existing := maps.Keys(allRoles)
		for name := range req.Roles {
			if namespacing.FindShortNameConflict(existing, name) != namespacing.NoRoleConflict {
				return fmt.Errorf("%w: roles already exist", ErrBadRequest)
			}
		}
	}

	if req.Version < cmd.RBACLatestCommandPolicyVersion {
		for roleName, policies := range req.Roles {
			permissions := []*authorization.Policy{}
			for _, p := range policies {
				permissions = append(permissions, &p)
			}
			// remove old permissions
			if err := m.authZ.RemovePermissions(roleName, permissions); err != nil {
				return err
			}
		}
	}

	reqMigrated, err := migrateUpsertRolesPermissions(req)

View on GitHub (pinned to 75aa4b6d11)

Solutions

  1. Check whether the role already exists (GET roles) before creating, and skip or turn the create into an update of permissions
  2. Pick a role name whose short name is unique across all namespaces (namespacing enforces short-name uniqueness, not just exact match)
  3. Make create calls idempotent: on this error treat the role as existing and continue instead of retrying the create
  4. If a partial multi-role request failed, remove the conflicting role names and resubmit only the new ones

Example fix

// before
client.Roles().Creator().WithRole(&schema.Role{Name: "viewer", Permissions: perms}).Do(ctx) // fails: roles already exist
// after
existing, _ := client.Roles().AllGetter().Do(ctx)
if _, ok := existing["viewer"]; !ok {
    client.Roles().Creator().WithRole(&schema.Role{Name: "viewer", Permissions: perms}).Do(ctx)
}
Defensive patterns

Strategy: validation

Validate before calling

roles, err := client.Roles().AllGetter().Do(ctx)
if err != nil { return err }
if _, exists := roles["viewer"]; exists {
    // skip create or update permissions instead
}

Type guard

func roleExists(roles map[string]schema.Role, name string) bool {
    _, ok := roles[name]
    return ok
}

Try / catch

if err := client.Roles().Creator().WithRole(r).Do(ctx); err != nil && strings.Contains(err.Error(), "roles already exist") {
    // treat as existing, continue
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of weaviate/weaviate@75aa4b6d11 (2026-09-04). Data as JSON: /api/errors/6272dd9d647b3c30. Report an issue: GitHub.