tl;dr: There's a bad bug in the SharePoint Subscription Edition 23H2 Feature Update, and I wrote a Powershell script to patch your 16-hive until Microsoft issues a patch of their own. Script at the bottom.
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
.
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.
Observe the lag in rendering the theme UI, and look at the Performance call tree(!)
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. 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
<#
.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" }
Top comments (1)
Thanks so much for this post. It was a huge help to us. For anyone else stumbling upon this in the future: the problem appears to be fixed by the December 2023 CU.