SharePoint SE 23H2 bug on themed sites - long page load times & faulty theme previews - fix included 👇
Tags: sharepoint, onprem, bugfix, hack
Published: 2023-10-13 12:28:26

### tl;dr: There's a bad bug in the [SharePoint Subscription Edition 23H2 Feature Update](https://learn.microsoft.com/en-us/sharepoint/what-s-new/new-and-improved-features-in-sharepoint-server-subscription-edition-23h2-release), and I wrote a Powershell script to patch your 16-hive until Microsoft issues a patch of their own. [Script at the bottom](#patch).  


A colleague of mine encountered issues in a client's onprem SharePoint Subscription Edition farm last week, immediately following the deployment of the 23H2 update.

### The Bug(s)
While attempting to preview themes, an error message appears: `There was an error when trying to preview the theme`.
![theme error](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p7wuqytnlz8en2laihm4.png)

On its own not a big deal. Applying the theme works, seems to be just the previewing that's borked.  
However, the real bug happens when you apply a theme, then try navigating between themed sites. Significant delays — up to 10 seconds, and no error message in the console or UI.  

Notably, these delays do _not_ occur when navigating between pages within the same site, as theme loading is site-specific. See e.g. `localStorage.odThemecachetoken`, which has a site specific value a là `"/sites/siteurl/_catalogs/theme/Themed/SOME_HEX_ID"`.  
clearing one or more of the `localStorage.od*` properties, or just `localStorage.clear()` triggers the bug in the same way as cross-site navigation does.

![localStorage](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ulid5jayt0z28kzuor8u.png)

Observe the lag in rendering the theme UI, and look at the Performance call tree(!)

![recursion iceberg](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lt5a29zqt0a3yh9ft1c5.png) _Classic recursion-iceberg_


![_handleThemeChange recursion](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/19ub21p7aol0j4csu1dp.png) _It's handleThemeChange all the way down_

### The nitty gritty
On closer examination, the delays look to be due to a recursive js-bug, triggering `Maximum call stack size exceeded` errors. This issue is traced back to a bug in the `sp-pages-assembly.js` script, with uniqueId `pxHxh` (most SharePoint js-files get loaded with a `uniqueId` query string suffix which is unique to the patch version).  
The bug affects the handling of new `effects` and `spacing` theme properties in the `createTheme` and `spLoadTheme` functions.

If you're curious to see the recursion in action, put a breakpoint in `sp-pages-assembly.js`, on the line containing the following: `Object(N.isEqual)(Object(M.createTheme)(e),e)||Object(k.spLoadTheme)(e)` and step from there.

Anyway, after some painful debugging of mini- and uglified SharePoint scripts, I found out that adding default values to the `effects` and `spacing` properties in the default theming object (which already contains `palette, fonts, semanticColors, isInverted` and `disableGlobalClassNames` properties), the recursion-bug does doesn't trigger.

To make it easier to implement, I wrote a [small PowerShell script](https://gist.github.com/damsleth/fba9b67318c1a966fdca67d32f0a7543). It does a quick-and-dirty find/replace operation in all `sp-pages-assembly.js` files in the 16-hive. By running this script on all web front ends, the theme preview function should operate correctly, and page load times should be back to normal

### A Word of Caution

Modding JS files in the 16-hive is not advisable under normal circumstances. It's a hack. It's **bad**. It will make your average SharePoint admin wince. We did this shit in the late 00's, together with custom master pages and GAC'ed .dlls, and it usually ended badly.  
The modifications introduced by the PowerShell script are temporary and will be overwritten by Microsoft’s next CU. Hopefully, Microsoft will release a fix for this bug in the upcoming update, rendering this makeshift solution unnecessary.

Full script below in case you don't like github gists for some reason 👇


## Patch
``` powershell
<#
.SYNOPSIS
    This script patches a recursion bug in the file 'sp-pages-assembly.js', in the 23H2 release of SharePoint Subscription Edition.
.COMPONENT
    SharePoint Server Subscription Edition 23H2
.NOTES
    This script is provided as-is, without any warranty or support what so ever.
    It is not supported by Microsoft, and is provided as a courtesy to the community.
    Use at your own risk.
.DESCRIPTION
    This patch fixes a recursion bug in the spLoadTheme and createTheme functions in sp-pages-assembly.js,
    a file found in C:\Program Files\Common Files\microsoft shared\Web Server Extensions\16\TEMPLATE\LAYOUTS\Next\spclient\[locale]\
    This bug causes cross site navigation on sites with themes applied to trigger a Maximum Call Stack Size Exceeded error,
    making cross site navigation extremely slow.

    RUN THIS SCRIPT ON ALL WEB FRONT ENDS IN YOUR FARM, AFTER INSTALLING THE 23H2 UPDATE.
    C:\PS> Patch-SpPagesAssembly.ps1 -DryRun:$false

    Bug reported by @havardberg on 30.09.2023.
    Patch by @damsleth on 04.10.2023
    
    STEPS THIS SCRIPT PERFORMS:
    cd to C:\Program Files\Common Files\microsoft shared\Web Server Extensions\16\TEMPLATE\LAYOUTS\Next\spclient\
    finds all js-files with name sp-pages-assembly in all subfolders, e.g. \nb-no, \en-us, \default
    foreach file, 
    backs up the sp-pages-assembly file to a file with the same name but with .bak appended
    replaces the following text:
    '!!e.disableGlobalClassNames}' 
    with the following (these are default values also found elsewhere in sp-pages-assembly.js)
    '!!e.disableGlobalClassNames, effects:e.effects || {
      elevation4:"005px0rgba(0,0,0,.4)",
      elevation8:"005px0rgba(0,0,0,.4)",
      elevation16:"005px0rgba(0,0,0,.4)",
      elevation64:"005px0rgba(0,0,0,.4)",
      roundedCorner2:"0px"
    },
    spacing:e.spacing || {l1:"20px",l2:"32px",m:"16px",s1:"8px",s2:"4px"}}'.
    
    If the file was patched and -DryRun is $false, the file is saved.

    THIS PATCH IS NOT SUPPORTED BY MICROSOFT, AND IS PROVIDED AS-IS, WITHOUT ANY WARRANTY OR SUPPORT WHAT SO EVER.
    DO NOT RUN POWERSHELL SCRIPTS ON YOUR PRODUCTION SYSTEMS WITHOUT TESTING THEM FIRST.
    
.PARAMETER DryRun
    Whether to actually patch the files or just list the files that would be patched
.EXAMPLE
    C:\PS> .\Patch-SpPagesAssembly.ps1 -DryRun:$false
.NOTES
    Author: Carl Joakim Damsleth (github.com/damsleth)
    Date:   October 4, 2023
.LINK
    Bug reports: https://learn.microsoft.com/en-us/answers/questions/1373533/sharepoint-server-subscription-changing-look-and-f
    This gist: https://gist.github.com/damsleth/fba9b67318c1a966fdca67d32f0a7543
#>
Param(
  [Parameter(Mandatory = $false, HelpMessage = "Whether to actually patch the files or just list the files that would be patched")]
  [boolean]$dryrun = $true
)

$searchTxt = '!!e.disableGlobalClassNames}'
$replaceTxt = '!!e.disableGlobalClassNames,effects:e.effects||{elevation4:"005px0rgba(0,0,0,.4)",elevation8:"005px0rgba(0,0,0,.4)",elevation16:"005px0rgba(0,0,0,.4)",elevation64:"005px0rgba(0,0,0,.4)",roundedCorner2:"0px"},spacing:e.spacing||{l1:"20px",l2:"32px",m:"16px",s1:"8px",s2:"4px"}}'
$spClientFolder = "C:\Program Files\Common Files\microsoft shared\Web Server Extensions\16\TEMPLATE\LAYOUTS\Next\spclient"

if ($(Get-Location).Path -ne $spClientFolder) {
  Write-Warning "`nChanging directory to`n$spClientFolder"
  Set-Location -Path $spClientFolder
}
$files = Get-ChildItem -Path .\*\sp-pages-assembly.js -Recurse
$i = $fixed = 0

if ($dryrun) { Write-Warning "`nDry run only, not patching anything.`nRerun with -dryrun `$false to patch files`n`n" }
$files | ForEach-Object {
  $i++
  Write-Host "`n$i/$($files.count) Processing '$($_.Directory.Name+"\"+$_.Name)'"
  $file = Get-Content $_.FullName
  if ([regex]::Match($file, $searchTxt).Success) {
    Write-Host "File is unpatched"
    if (!$dryrun) {
      Write-Host "Backing up '$($_.Directory.Name+"\"+$_.Name)' to '$($_.Directory.Name+"\"+$_.Name).bak'"
      Copy-Item $_.FullName "$($_.FullName).bak"
      Write-Host "Patching $($_.FullName)"
      $file -replace $searchTxt, $replaceTxt | Set-Content $_.FullName
    }
    else {
      Write-Host "Dryrun only, not backing up or patching '$($_.Directory.Name+"\"+$_.Name)'"
    }
    $fixed++
  }
  else {
    Write-Host "File '$($_.Directory.Name+"\"+$_.Name)' looks like it's already patched, skipping"
  }
}
if ($dryrun) { Write-Host "`n`nDry run finished, $fixed of $i sp-pages-assembly.js files found would have been patched" }
else { Write-Host "`nDone patching $fixed of $i sp-pages-assembly.js files" }
```