[BUG?] Transaction system causing GC related crash when using widget reflector

Hi,

Whenever a designer

  1. Starts the game in PIE
  2. Edits a field using the widget reflector
  3. End the game

We get a GC related crash.

We’re creating widgets via UUserWidget::CreateWidgetInstance and CreateWidget on the UserWidget themselves.

The best i can tell, what’s happening is

  1. All UserWidgets are RF_Transactional by default
  2. SWidgetReflector passes widget to details panel
  3. Editing fields in details panels triggers a transaction
  4. ref to widget get’s stored in transaction system
  5. ref isn’t considered transient, so GC blows up at game end

EditorTransaction has some guards around objects being PIE, but it doesn’t seem to be catching these. I believe because CreateWidget really wants to make the GameInstance the outer for the widget, even if you pass in World or PlayerController.

I was able to work around the issue by adding, or by forcing RF_TRANSACTIONAL off for the widgets, or by creating the widget without using the CreateWidget function. All these feel kind of wrong though.


bool FTransaction::FObjectRecord::ContainsPieObject() const
{
auto IsObjectPIEScoped = 
(const UObject* Obj) → bool
{
if (!Obj)
{
return false;
}
if (Obj->GetOutermost()->HasAnyPackageFlags(PKG_PlayInEditor))
{
return true;
}
if (const UWorld* World = Obj->GetWorld(); IsValid(World) && World->IsPlayInEditor())
{
return true;
}
return false;
};

if (IsObjectPIEScoped(Object.Get()))
{
	return true;
}
// Rest of code...

Engine Version: 5.6
Trace and GC error log:

Assertion failed: false [File:G:\j\de_ugs\Engine\Source\Editor\UnrealEd\Private\PlayLevel.cpp] [Line: 544]
Object ‘BP_CLASS_THAT_EXTENDS_OUR_GAME_INSTANCE_C /Engine/Transient.UnrealEdEngine_0:BP_CLASS_THAT_EXTENDS_OUR_GAME_INSTANCE_C_0’ from PIE level still referenced. Shortest path from root:  (root)  UnrealEdEngine /Engine/Transient.UnrealEdEngine_0
 → TObjectPtr UEditorEngine::Trans =  TransBuffer /Engine/Transient.TransBuffer_0
 → TransBuffer /Engine/Transient.TransBuffer_0::AddReferencedObjects((Garbage)  BP_CLASS_THAT_EXTENDS_OUR_GAME_INSTANCE_C /Engine/Transient.UnrealEdEngine_0:BP_CLASS_THAT_EXTENDS_OUR_GAME_INSTANCE_C_0)
^ UE::ReferenceChainSearch::FReferenceInfoSearch::HandleObjectReference() 

^ UE::ReferenceChainSearch::TReferenceSearchBaseUE::ReferenceChainSearch::FReferenceInfoSearch::FCollector<1>::HandleObjectReference() 

^ UE::Transaction::FPersistentObjectRef::AddReferencedObjects() 

^ FTransaction::FObjectRecord::AddReferencedObjects() 

^ FTransaction::AddReferencedObjects() 

^ UTransBuffer::AddReferencedObjects() 

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
^ This reference is preventing the old BP_CLASS_THAT_EXTENDS_OUR_GAME_INSTANCE from being GC’d ^

0x00007ffcf4db3458 UnrealEditor-Core.dll!FDebug::CheckVerifyFailedImpl2() 

0x00007ffcf19e2b1a UnrealEditor-UnrealEd.dll!UEditorEngine::EndPlayMap() 

0x00007ffcf12a0980 UnrealEditor-UnrealEd.dll!UEditorEngine::Tick() 

0x00007ffcf1f713f6 UnrealEditor-UnrealEd.dll!UUnrealEdEngine::Tick() 

0x00007ff7c1e29ce4 UnrealEditor.exe!FEngineLoop::Tick() 

0x00007ff7c1e4e5ac UnrealEditor.exe!GuardedMain() 

0x00007ff7c1e4e6ba UnrealEditor.exe!GuardedMainWrapper() 

0x00007ff7c1e5209e UnrealEditor.exe!LaunchWindowsStartup() 

0x00007ff7c1e64e44 UnrealEditor.exe!WinMain() 

0x00007ff7c1e680fa UnrealEditor.exe!__scrt_common_main_seh() 

0x00007ffdf717e957 KERNEL32.DLL!UnknownFunction