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, ErrOrgNotFound

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Request a strictly higher-permission role than the member's current role.
  2. Check the member's current role first and disable the upgrade action if requestedRole <= current.
  3. 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

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


AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02). Data as JSON: /api/errors/492d6ed28c26fbdd. Report an issue: GitHub.