{"record":{"id":"492d6ed28c26fbdd","repo":"Tencent/WeKnora","slug":"cannot-request-upgrade-to-same-or-lower-role","errorCode":null,"errorMessage":"cannot request upgrade to same or lower role","messagePattern":"cannot request upgrade to same or lower role","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"internal/application/service/organization.go","lineNumber":624,"sourceCode":"\t}\n\treturn org.OwnerTenantID == tenantID\n}\n\n// generateInviteCode generates a random 16-character invite code\nfunc generateInviteCode() string {\n\tbytes := make([]byte, 8)\n\t_, _ = rand.Read(bytes)\n\treturn hex.EncodeToString(bytes)\n}\n\n// ----------------\n// Join Requests\n// ----------------\n\nvar (\n\tErrPendingRequestExists    = errors.New(\"pending request already exists\")\n\tErrJoinRequestNotFound     = errors.New(\"join request not found\")\n\tErrCannotUpgradeToSameRole = errors.New(\"cannot request upgrade to same or lower role\")\n\tErrAlreadyAdmin            = errors.New(\"tenant is already an admin\")\n)\n\n// SubmitJoinRequest submits a request for the caller's tenant to join an organization.\n// Dedup is now per-tenant: any user from a tenant already with a pending join\n// request is rejected (the same tenant can't queue two simultaneous joins).\nfunc (s *organizationService) SubmitJoinRequest(ctx context.Context, orgID string, userID string, tenantID uint64, message string, requestedRole types.OrgMemberRole) (*types.OrganizationJoinRequest, error) {\n\tlogger.Infof(ctx, \"Tenant %d (rep user %s) submitting join request for organization %s\", tenantID, userID, orgID)\n\n\texisting, err := s.orgRepo.GetPendingRequestByTenantAndType(ctx, orgID, tenantID, types.JoinRequestTypeJoin)\n\tif err == nil && existing != nil {\n\t\treturn nil, ErrPendingRequestExists\n\t}\n\n\torg, err := s.orgRepo.GetByID(ctx, orgID)\n\tif err != nil {\n\t\tif errors.Is(err, repository.ErrOrganizationNotFound) {\n\t\t\treturn nil, ErrOrgNotFound","sourceCodeStart":606,"sourceCodeEnd":642,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/application/service/organization.go#L606-L642","documentation":"ErrCannotUpgradeToSameRole is a sentinel returned by RequestRoleUpgrade when the requested role does not grant more permissions than the tenant's current role, or equals it. The guard is `!requestedRole.HasPermission(member.Role) || requestedRole == member.Role`, so lateral or downward requests are rejected.","triggerScenarios":"Calling RequestRoleUpgrade with a requestedRole equal to the member's current role, or a lower-permission role (e.g. a member at role X requesting role Y where Y.HasPermission(X) is false).","commonSituations":"UIs not hiding the 'upgrade' action for users already at the target role, role enums reordered so the 'upgrade' endpoint is fed the current role, or duplicate form submissions after the first upgrade was approved.","solutions":["Request a strictly higher-permission role than the member's current role.","Check the member's current role first and disable the upgrade action if requestedRole <= current.","Handle errors.Is(err, ErrCannotUpgradeToSameRole) with a clear message to the user."],"exampleFix":"// before\nif member.Role == types.OrgRoleAdmin { /* skip, already admin */ }\nsvc.RequestRoleUpgrade(ctx, orgID, userID, tenantID, member.Role) // same role\n// after\nif types.OrgRoleAdmin.HasPermission(member.Role) && member.Role != types.OrgRoleAdmin {\n    svc.RequestRoleUpgrade(ctx, orgID, userID, tenantID, types.OrgRoleAdmin)\n}","handlingStrategy":"validation","validationCode":"if !requestedRole.HasPermission(member.Role) || requestedRole == member.Role {\n    return fmt.Errorf(\"requested role must be strictly higher than current role %s\", member.Role)\n}","typeGuard":"func isUpgrade(current, requested types.OrgMemberRole) bool {\n    return requested.HasPermission(current) && requested != current\n}","tryCatchPattern":"if errors.Is(err, organization.ErrCannotUpgradeToSameRole) {\n    return fmt.Errorf(\"choose a role higher than your current role\")\n}","preventionTips":["Fetch the member's current role before showing upgrade options.","Only offer roles strictly above the current one in the UI.","Handle the sentinel explicitly rather than by message text."],"tags":["role-permissions","validation","organization","go"],"backgroundTag":"invalid-role-transition","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}