Bundled PSO Spc cache fails to load during cook and prevents packaging with it

Summary

Cooking fails with an assert while building the bundled PSO spc. The .spc file written by the “expand” step cannot be read back by the “build” step in the same engine build, so the cook dies at the very end with no output.

This occurs in engine source release and main epic engine release below is the revision the change was from.

(d3048e58e291 / CL 52102159, 2026-03-24)

What Type of Bug are you experiencing?

Foundation (C++ Tools, Profiling, & Pipeline)

Steps to Reproduce

  1. On 5.8, use a project with the shader library and stable shader keys enabled.
  2. Record a PSO cache from a run, so you have a .rec.upipelinecache plus the .shk stable key files.
  3. Run the expand step to produce a .spc file. This succeeds:
    UnrealEditor-Cmd.exe .uproject -run=ShaderPipelineCacheTools expand “<…>/.rec.upipelinecache" "<…>/.shk” “_PCD3D_SM6.spc”
  4. Put the .spc in /Build/Windows/PipelineCaches/
  5. Cook or package for Windows. It crashes at the end of the cook.

Expected Result

The .spc file written by the expand step loads correctly in the build step, and the cook finishes and produces a .stable.upipelinecache.
A file saved by the same engine build should always be readable by the same build.

Observed Result

The build step asserts and the cook fails with ExitCode=3.

Affects Versions

5.8

Platform(s)

Windows

Upload an image

For crash reports, include your callstack

Assertion failed: PermDescriptor.ActivePerSlot[SF_Compute] [File:…\Engine\Source\Runtime\RenderCore\Private\PipelineCacheUtilities.cpp] [Line: 205]
UnrealEditor-RenderCore.dll!UE::PipelineCacheUtilities::Private::SanityCheckActiveSlots() [PipelineCacheUtilities.cpp:205]
UnrealEditor-RenderCore.dll!UE::PipelineCacheUtilities::LoadStablePipelineCacheFile() [PipelineCacheUtilities.cpp:804]
UnrealEditor-UnrealEd.dll!BuildPSOSC() [ShaderPipelineCacheToolsCommandlet.cpp:2600]
UnrealEditor-UnrealEd.dll!UShaderPipelineCacheToolsCommandlet::StaticMain() [ShaderPipelineCacheToolsCommandlet.cpp:2910]
UnrealEditor-UnrealEd.dll!UCookOnTheFlyServer::CreatePipelineCache() [ShaderLibraryCooking.cpp:536]
UnrealEditor-UnrealEd.dll!UCookOnTheFlyServer::CookByTheBookFinishedInternal() [CookOnTheFlyServer.cpp:9799]

Additional Notes

FIX WE ARE RUNNING LOCALLY (verified on 5.8.1-release)

File: Engine/Source/Runtime/RenderCore/Private/PipelineCacheUtilities.cpp

Line 598, in SaveStablePipelineCacheFile():
(-) WriteVarUIntToArchive(MemWriter, NewPSO.UsageMask);
(+) WriteVarUIntToArchive(PlainMemWriter, NewPSO.UsageMask);

Line 799, in LoadStablePipelineCacheFile():
(-) OriginalPSOs[PermutationGroupIdx].UsageMask = ReadVarUIntFromArchive(MemReader);
(+) OriginalPSOs[PermutationGroupIdx].UsageMask = ReadVarUIntFromArchive(PlainMemReader);

(see image for current local fix)

Confirming this on 5.8.1 (launcher build CL-56057345). So it is not fixed in the hotfix. Different project, Android/Vulkan (`SF_VULKAN_ES31_ANDROID`), packaging for standalone XR headsets.

Two things that might help pin it down.

1. It is not engine-version skew, and the write side is fine. Our `.spc` was originally produced under 5.8.0, so we re-ran `expand` under 5.8.1 to have the engine write a fresh one. `expand` succeeds (exit 0, reads the older `.rec`/`.shk` without complaint). Feeding that brand-new file straight back into `build` crashes the same way, ~60 seconds after it was written. The defect is purely on read-back.

2. The crash signature is unstable, which makes this easy to mis-triage. Three different asserts, all the same corrupted stream tripping whichever guard comes first:

Where Signature
5.8.0 `checkNoEntry()` - `PipelineFileCache.cpp:1424`
5.8.1 `Trying to resize TArray to an invalid size of 4294967287` - `ContainerHelpers.cpp:8`, in `RenderCore`
this thread `PermDescriptor.ActivePerSlot[SF_Compute]` - `PipelineCacheUtilities.cpp:205`