SharePoint Site Swap Fails – But a Fallback Position

When we build a new Home Site in a client tenant, usually for a new Intranet, we generally build that new site off in a corner, so it’s not exposed or in the way while we build it. This process can take a few weeks, months or even longer, and while we’re working on things, we want to leave the existing sites rerunning as they are. Once the new site – which I generally put at /sites/NewIntranet – is finished, we can do a Site Swap to move it into the root site at launch.

In this case, we were trying to do a site swap using the Replace Site UI in the SharePoint Admin Center. We had the new Communication site we wanted to swap into the root all set. I’ve done this process many times with no issues.

When I entered the URL for the site we wanted to swap into the root, I got the message saying “✅ Site is ready”.

After clicking the Save button, the process started and soon thereafter, I got the message “Root site couldn’t be replaced”.

I also tried the swap with the PowerShell cmdlets Invoke-PnPSiteSwap and Invoke-SPOSiteSwap. In both cases, I got a message like this:

That is…

Invoke-SPOSiteSwap : The new site name is too long using URL https://tenantname.sharepoint.com/sites/ArchiveOldSite. The path for all files and folders in the site cannot exceed 400 characters At line:1 char:5 + Invoke-SPOSiteSwap ` + ~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (:) [Invoke-SPOSiteSwap], ServerException + FullyQualifiedErrorId : Microsoft.SharePoint.Client.ServerException,Microsoft.Online.SharePoint.PowerShell.SwapSite

With a bunch of Binglage, I saw that many people had run into this problem, and it may have had nothing to do with URL lengths. That said, we did have some pretty deep folder structures.

Fallback Position

We didn’t want to abort at the 11th hour if we could help it. As I thought about it, I realized I could go to a fallback position that isn’t horrible and it wouldn’t prevent me from doing the site swap afterward, once we worked out the issue.

Here’s what we did:

  • Set the /sites/NewIntranet site as the Home Site
  • This gave us the Global Navigation capability, so we could set up the App Bar to show the Intranet navigation nodes in place of the SharePoint Home Page.
  • Edited the home page in the current root site (an old Team Site, of course) to display launch info and a button that took us to the New Intranet home page.
  • We shared the link to the New Intranet with the organization, so they can go directly to the /sites/NewIntranet site.
  • I also put in a ticket with Microsoft because – well – that’s something you’re supposed to do.

While we waited for Microsoft Support to get back to us, we were in a decent position. The new Intranet was “launched” and could be used. We can do the site swap later, and everything will just redirect and the behavior will be the same. Presto! 

Solving the Problem

But what’s the actual problem, and how can we / could we have solved it? After about 3 weeks of back and forth with support, they ended up telling me that we had files with paths that were too long. Um, ok. I could pretty much tell that based on the error message I got, but I was hoping Microsoft would have a smart recommendation about mitigating the actual problem. Unfortunately, not.

Remember that I started by using the nice UI provided to make the swap, and the error I got was the incredibly unhelpful “Root site couldn’t be replaced”. There was no information there for me to work with. Only after I ran the PowerShell approach to swap the site that I got some clues.

Should I have known there would be an issue? Maybe, sure. That’s partially on me. But what about organizations that don’t have someone who can quickly pivot to PowerShell or have been working with SharePoint for decades?

The Manage home site page in the SharePoint Admin Center should give much better information when a Site Swap fails. There is zero information about what to do to resolve the failure in that UI or in the Modernize your root site – SharePoint in Microsoft 365 | Microsoft Learn article. It would be ideal if there were:

  • A “test” option in the UI that tells us whether the Site Swap will work. When we click the Save button – it’s for real. There’s no way to know if there will be a problem until we’re ready to do the swap for real.
  • Information about common issues in the above article, along with ways to mitigate the issues. Now that I’ve run into this issue and seen by searching that lots of other have run into it as well, it should be spelled out very clearly, along with how to get around it.

Thus, this post – for those of you who might run into this issue at the 11th hour like I did. The fallback position can get you launched, but then what?

One of the things the Microsoft support folks gave me was a script to find files with long paths. I didn’t get much explanation, and the script was set to look for files with URLs greater than 218 characters. I have no idea what the significance of that number is, but it reported on probably half the files in the root site.

Here’s the script as I received it from support. You’d change the parameters at the top and run it. It gives you a CSV file listing all the files with long URLs, based on the $MaxUrlLength. That value you should choose there will depend on where you plan to move the site. For example, if you want to swap it into https://mytenant.sharepoint.com/sites/A (which is the shortest possible URL in mytenant), you could set $MaxUrlLength to 362:

  • The maximum URL length is 400
  • The length of https://mytenant.sharepoint.com/sites/A is 38
  • Thus, you could move files with URL lengths of 400 – 38 = 362 URL lengths into that site.

Unfortunately, the length of your organization’s name – in my example, that’s mytenant – matters. If you have a longer organization name, you have a bigger problem.

The script outputs a CSV file with a list of the files with long URLs and details about each, as defined in $global:LongURLInventory. Using that list, you can perform the mitigation you need. In my case, we’ve got something like 12,000 files with long URLs. I’ll be writing a script that uses ShareGate to copy the files into https://mytenant.sharepoint.com/sites/A and then delete them from their original locations. At that point, we’ll be able to do the site swap without any issues. 🤞🏻

#Parameters
$SiteURL = "https://crescent.SharePoint.com/sites/Marketing"
$MaxUrlLength = 218
$CSVPath = "C:\Temp\LongURLInventory.csv"
$global:LongURLInventory = @()
$Pagesize = 2000

#Function to scan and collect long files
Function Get-PnPLongURLInventory
{
    [cmdletbinding()]
    param([parameter(Mandatory = $true, ValueFromPipeline = $true)] $Web)
  
    Write-host "Scanning Files with Long URL in Site '$($Web.URL)'" -f Yellow
    If($Web.ServerRelativeUrl -eq "/")
    {
        $TenantURL= $Web.Url
    }
    Else
    {
        $TenantURL= $Web.Url.Replace($Web.ServerRelativeUrl,'')
    }
     
    #Get All Large Lists from the Web - Exclude Hidden and certain lists
    $ExcludedLists = @("Form Templates", "Preservation Hold Library","Site Assets", "Pages", "Site Pages", "Images",
                            "Site Collection Documents", "Site Collection Images","Style Library")
                              
    #Get All Document Libraries from the Web
    $Lists= Get-PnPProperty -ClientObject $Web -Property Lists
    $Lists | Where-Object {$_.BaseType -eq "DocumentLibrary" -and $_.Hidden -eq $false -and $_.Title -notin $ExcludedLists -and $_.ItemCount -gt 0} -PipelineVariable List | ForEach-Object {
        #Get Items from List  
        $global:counter = 0;
        $ListItems = Get-PnPListItem -List $_ -PageSize $Pagesize -Fields Author, Created, File_x0020_Type -ScriptBlock { Param($items) $global:counter += $items.Count; Write-Progress -PercentComplete ($global:Counter / ($_.ItemCount) * 100) -Activity "Getting List Items of '$($_.Title)'" -Status "Processing Items $global:Counter to $($_.ItemCount)";}
        $LongListItems = $ListItems | Where { ([uri]::EscapeUriString($_.FieldValues.FileRef).Length + $TenantURL.Length ) -gt $MaxUrlLength }
        Write-Progress -Activity "Completed Retrieving Items from List $($List.Title)" -Completed
                 
        If($LongListItems.count -gt 0)
        {
            #Get Root folder of the List
            $Folder = Get-PnPProperty -ClientObject $_ -Property RootFolder
            Write-host "`tFound '$($LongListItems.count)' Items with Long URLs at '$($Folder.ServerRelativeURL)'" -f Green
 
            #Iterate through each long url item and collect data          
            ForEach($ListItem in $LongListItems)
            {
                #Calculate Encoded Full URL of the File
                $AbsoluteURL =  "$TenantURL$($ListItem.FieldValues.FileRef)"
                $EncodedURL = [uri]::EscapeUriString($AbsoluteURL)
  
                    #Collect document data
                    $global:LongURLInventory += New-Object PSObject -Property ([ordered]@{
                        SiteName  = $Web.Title
                        SiteURL  = $Web.URL
                        LibraryName = $List.Title
                        LibraryURL = $Folder.ServerRelativeURL
                        ItemName = $ListItem.FieldValues.FileLeafRef
                        Type = $ListItem.FileSystemObjectType
                        FileType = $ListItem.FieldValues.File_x0020_Type
                        AbsoluteURL = $AbsoluteURL
                        EncodedURL = $EncodedURL
                        UrlLength = $EncodedURL.Length                     
                        CreatedBy = $ListItem.FieldValues.Author.LookupValue
                        CreatedByEmail  = $ListItem.FieldValues.Author.Email
                        CreatedAt = $ListItem.FieldValues.Created
                        ModifiedBy = $ListItem.FieldValues.Editor.LookupValue
                        ModifiedByEmail = $ListItem.FieldValues.Editor.Email
                        ModifiedAt = $ListItem.FieldValues.Modified                       
                    })
                }
            }
        }
}
 
#Connect to Site collection
Connect-PnPOnline -Url $SiteURL -Interactive
   
#Call the Function for all Webs
Get-PnPSubWeb -Recurse -IncludeRootWeb | ForEach-Object { Get-PnPLongURLInventory $_ }
  
#Export Documents Inventory to CSV
$Global:LongURLInventory | Export-Csv $CSVPath -NoTypeInformation
Write-host "Report has been Exported to '$CSVPath'"  -f Magenta

Similar Posts

4 Comments

  1. Wow, that was an interesting article. Thanks Marc for sharing all of this, and the excellent info on how you investigated, and then solved this issue.

    Much appreciated, this will help a lot of SharePoint admins! ❤️

  2. We ran into the issue where the site swap feature never showed up on our tenant. We have an automatic redirect on the root site using a News Link item. The News Link is still a Site Page and we set the News Link Site page as the home page for the root site – instant redirect.

  3. Hi Marc!

    >> Edited the home page in the current root site (an old Team Site, of course) to display launch info and a button that took us to the New Intranet home page.

    On classic pages years before we added CEWP with:

    window.location.href = “https://portal.sharepoint.com/sites/newSiteCollection”

    on it..

    With Modern Script Editor maybe there will be a time lag and page will blink.. 🤷‍♂️

    Best regards, Gennady

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.