Tencent/WeKnora · warning
cannot request upgrade to same or lower role
Error message
cannot request upgrade to same or lower role
What it means
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.
Source
Thrown at internal/application/service/organization.go:624
}
return org.OwnerTenantID == tenantID
}
// generateInviteCode generates a random 16-character invite code
func generateInviteCode() string {
bytes := make([]byte, 8)
_, _ = rand.Read(bytes)
return hex.EncodeToString(bytes)
}
// ----------------
// Join Requests
// ----------------
var (
ErrPendingRequestExists = errors.New("pending request already exists")
ErrJoinRequestNotFound = errors.New("join request not found")
ErrCannotUpgradeToSameRole = errors.New("cannot request upgrade to same or lower role")
ErrAlreadyAdmin = errors.New("tenant is already an admin")
)
// SubmitJoinRequest submits a request for the caller's tenant to join an organization.
// Dedup is now per-tenant: any user from a tenant already with a pending join
// request is rejected (the same tenant can't queue two simultaneous joins).
func (s *organizationService) SubmitJoinRequest(ctx context.Context, orgID string, userID string, tenantID uint64, message string, requestedRole types.OrgMemberRole) (*types.OrganizationJoinRequest, error) {
logger.Infof(ctx, "Tenant %d (rep user %s) submitting join request for organization %s", tenantID, userID, orgID)
existing, err := s.orgRepo.GetPendingRequestByTenantAndType(ctx, orgID, tenantID, types.JoinRequestTypeJoin)
if err == nil && existing != nil {
return nil, ErrPendingRequestExists
}
org, err := s.orgRepo.GetByID(ctx, orgID)
if err != nil {
if errors.Is(err, repository.ErrOrganizationNotFound) {
return nil, ErrOrgNotFoundView on GitHub (pinned to 988cbb0330)
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.
Example fix
// before
if member.Role == types.OrgRoleAdmin { /* skip, already admin */ }
svc.RequestRoleUpgrade(ctx, orgID, userID, tenantID, member.Role) // same role
// after
if types.OrgRoleAdmin.HasPermission(member.Role) && member.Role != types.OrgRoleAdmin {
svc.RequestRoleUpgrade(ctx, orgID, userID, tenantID, types.OrgRoleAdmin)
} Defensive patterns
Strategy: validation
Validate before calling
if !requestedRole.HasPermission(member.Role) || requestedRole == member.Role {
return fmt.Errorf("requested role must be strictly higher than current role %s", member.Role)
} Type guard
func isUpgrade(current, requested types.OrgMemberRole) bool {
return requested.HasPermission(current) && requested != current
} Try / catch
if errors.Is(err, organization.ErrCannotUpgradeToSameRole) {
return fmt.Errorf("choose a role higher than your current role")
} Prevention
- 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.
When it happens
Trigger: 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).
Common situations: 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.
Related errors
- member_limit must be >= 0
- tenant is already an admin
- model ID cannot be empty
- unknown credential field:
- pending request already exists
AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02).
Data as JSON: /api/errors/492d6ed28c26fbdd.
Report an issue: GitHub.