[5.8] Editor crashes on exit after Hot Reload changes a UWorldSubsystem's class layout (stale SubsystemArrayMap entry)

Summary

Editor crashes on exit (EXCEPTION_ACCESS_VIOLATION in UWorld::CleanupWorld) after a Hot Reload that changes the layout of a UWorldSubsystem class.

When a UWorldSubsystem’s header changes (e.g. a member is added) and the module is Hot Reloaded, the reload re-instances the existing subsystem object (Re-instancing <Class> after reload. in the log).

FSubsystemCollectionBase::SubsystemMap is reported to GC (AddStableReferenceMap), so reference replacement updates it to the new instance. The per-class cache FSubsystemCollectionBase::SubsystemArrayMap (TMap<UClass*, TUniquePtr<FSubsystemArray>>, holding raw TArray<USubsystem*>) is not exposed to GC and is not updated. It keeps pointing at the old instance, which is later garbage collected.

On editor exit, UWorld::CleanupWorldInternal calls SubsystemCollection.ForEachSubsystem(... PreDeinitialize ...). That iterates the stale SubsystemArrayMap entry and calls IsA() on the freed object, which crashes.

What Type of Bug are you experiencing?

Framework

Steps to Reproduce

  1. Create a C++ project and add a UWorldSubsystem subclass (any trivial implementation).
  2. Open the project in the Editor so the subsystem is created for the editor world.
  3. Add a member variable to the subsystem’s header (a layout change).
  4. Hot Reload the module from inside the Editor (not Live Coding). The log shows 1 class changed and Re-instancing <Class> after reload.
  5. Close the Editor.

Expected Result

The Editor closes normally.

Observed Result

The Editor crashes during exit with EXCEPTION_ACCESS_VIOLATION in UStruct::IsChildOf, called from FSubsystemCollectionBase::ForEachSubsystemOfClass in UWorld::CleanupWorldInternal. It reproduces every time. A .cpp-only change (no class layout change) does not crash.

Affects Versions

5.8

Platform(s)

Windows

For crash reports, include your callstack

Unhandled Exception: EXCEPTION_ACCESS_VIOLATION reading address 0x000000003f800038

UnrealEditor_CoreUObject!UStruct::IsChildOf() [Runtime\CoreUObject\Private\UObject\Class.cpp:2744]
UnrealEditor_Engine!UObjectBaseUtility::IsA<UClass *>() [Runtime\CoreUObject\Public\UObject\UObjectBaseUtility.h:650]
UnrealEditor_Engine!TFunctionRefCaller<FObjectSubsystemCollection<UWorldSubsystem>::ForEachSubsystem::<lambda_1>,void,USubsystem *>::Call() [Runtime\Core\Public\Templates\Function.h:292]
UnrealEditor_Engine!FSubsystemCollectionBase::ForEachSubsystemOfClass() [Runtime\Engine\Private\Subsystems\SubsystemCollection.cpp:179]
UnrealEditor_Engine!UWorld::CleanupWorldInternal() [Runtime\Engine\Private\World.cpp:6476]
UnrealEditor_Engine!UWorld::CleanupWorld() [Runtime\Engine\Private\World.cpp:6241]
UnrealEditor_UnrealEd!UEditorEngine::PreExit() [Editor\UnrealEd\Private\EditorEngine.cpp:1624]
UnrealEditor!FEngineLoop::Exit() [Runtime\Launch\Private\LaunchEngineLoop.cpp:5065]
UnrealEditor!GuardedMain() [Runtime\Launch\Private\Launch.cpp:204]
UnrealEditor!GuardedMainWrapper() [Runtime\Launch\Private\Windows\LaunchWindows.cpp:123]
UnrealEditor!LaunchWindowsStartup() [Runtime\Launch\Private\Windows\LaunchWindows.cpp:277]
UnrealEditor!WinMain() [Runtime\Launch\Private\Windows\LaunchWindows.cpp:338]

Additional Notes

Suggested fix: rebuild or clear SubsystemArrayMap after reinstancing (for example on FCoreUObjectDelegates::ReloadReinstancingCompleteDelegate), or update its entries through the same reference replacement as SubsystemMap.

Related: after the same kind of reload, the collection can also create a second instance from the new class while the re-instanced one still exists (this spawned duplicate gameplay actors in our project). Returning false from ShouldCreateSubsystem when GetClass() has CLASS_NewerVersionExists avoids the duplicate, but the exit crash remains.

Workaround: restart the Editor after changing a UWorldSubsystem header instead of Hot Reloading it.

Environment: UE 5.8 Launcher build, Windows 11.