Detaching from existing linker while object needs loading

Steps to Reproduce

During the cook process for our title (`UnrealEditor.exe ShooterGame -run=cook -targetplatform=WindowsClient`), we sometimes run into this error from `UObject::SetLinker`:

UE_CLOG(HasAnyFlags(RF_NeedLoad|RF_NeedPostLoad), LogUObjectLinker, Error,
	TEXT("Detaching from existing linker %s while object %s needs loading. Setting linker to %s."),
	*Existing.Linker->GetArchiveName(),
	*GetFullName(),
	LinkerLoad ? *LinkerLoad->GetDebugName() : TEXT("nullptr"));

I read [this existing [Content removed] which describes a very similar issue. I believe in our case `ResolveArchetypeInstances` is iterating over two derived classes of a blueprint. The first one allocates a subobject within the CDO of a shared parent, and the second one attempts to re-initialize the same object on top, which triggers the error as the existing subobject has the RF_NeedPostLoad flag at that point.

We are already using zenloader as recommended in the prior thread, and I am looking for a safe way to make our builds error-free without ignoring this class of SetLinker errors entirely. I am considering making a change to `StaticAllocateObject` within UObjectGlobals.cpp so that it would call `Obj->ClearFlags(RF_NeedPostLoad);` before calling `Obj->ConditionalBeginDestroy();`. Could a domain expert share their opinion on the safety/correctness of this change? Many thanks in advance.

[Attachment Removed]

The call stack leading us to the error is:

UObject::SetLinker(FLinkerLoad * LinkerLoad, int LinkerIndex, bool bShouldDetachExisting) Line 122
UObject::BeginDestroy() Line 965
UObject::ConditionalBeginDestroy() Line 1175
StaticAllocateObject(const UClass * InClass, UObject * InOuter, FName InName, EObjectFlags InFlags, EInternalObjectFlags InternalSetFlags, bool bCanRecycleSubobjects, bool * bOutRecycledSubobject, UPackage * ExternalPackage) Line 3604
StaticConstructObject_Internal(const FStaticConstructObjectParameters & Params) Line 4551
FObjectInstancingGraph::GetInstancedSubobject(UObject * SourceSubobject, UObject * CurrentValue, UObject * CurrentObject, EInstancePropertyValueFlags Flags) Line 240
FObjectInstancingGraph::InstancePropertyValue(UObject * SubObjectTemplate, UObject * CurrentValue, UObject * Owner, EInstancePropertyValueFlags Flags) Line 321
FObjectPropertyBase::InstanceSubobjects(void * Data, const void * DefaultData, UObject * InOwner, FObjectInstancingGraph * InstanceGraph) Line 65
UStruct::InstanceSubobjectTemplates(void * Data, const void * DefaultData, UStruct * DefaultStruct, UObject * Owner, FObjectInstancingGraph * InstanceGraph) Line 2200
FObjectInitializer::InstanceSubobjects(UClass * Class, bool bNeedInstancing, bool bNeedSubobjectInstancing) Line 4169
FObjectInitializer::PostConstructInit() Line 4078
[Inline Frame] FScriptIntegrationObjectHelper::PostConstructInitObject(FObjectInitializer &) Line 1598
FDeferredInitializationTrackerBase::ResolveDeferredInitialization(UObject * __formal, UObject * ArchetypeInstance) Line 2616
FDeferredInitializationTrackerBase::ResolveArchetypeInstances(UObject * InitDependency) Line 2574
[Inline Frame] FDeferredObjInitializationHelper::ResolveDeferredInitsFromArchetype(UObject *) Line 2895
FLinkerLoad::ResolveDeferredExports(UClass * LoadClass) Line 2228
FLinkerLoad::FinalizeBlueprint(UClass * LoadClass) Line 1924
FLinkerLoad::FinalizeBlueprint(UClass * LoadClass) Line 1877
FLinkerLoad::Preload(UObject * Object) Line 4662
FLinkerLoad::CreateExport(int Index) Line 5429
FLinkerLoad::IndexToObject(FPackageIndex Index) Line 5711
FLinkerLoad::CreateExport(int Index) Line 4981
FLinkerLoad::CreateExportAndPreload(int ExportIndex, bool bForcePreload) Line 3959
FLinkerLoad::LoadAllObjects(bool bForcePreload) Line 4137
 
[omitted for brevity, dozens of stack frames for nested asset loading]
	
LoadPackageInternal(UPackage * InOuter, const FPackagePath & PackagePath, unsigned int LoadFlags, FLinkerLoad * ImportLinker, FArchive * InReaderOverride, const FLinkerInstancingContext * InstancingContext, const FPackagePath * DiffPackagePath) Line 1798
LoadPackage(UPackage * InOuter, const FPackagePath & PackagePath, unsigned int LoadFlags, FArchive * InReaderOverride, const FLinkerInstancingContext * InstancingContext, const FPackagePath * DiffPackagePath) Line 2105
LoadPackage(UPackage * InOuter, const wchar_t * InLongPackageNameOrFilename, unsigned int LoadFlags, FArchive * InReaderOverride, const FLinkerInstancingContext * InstancingContext) Line 2078
UnrealEditor-UnrealEd.dll!UCookOnTheFlyServer::LoadPackageForCooking(UE::Cook::FPackageData & PackageData, UPackage * & OutPackage, UE::Cook::FPackageData * ReportingPackageData) Line 4142
UnrealEditor-UnrealEd.dll!UCookOnTheFlyServer::LoadPackageInQueue(UE::Cook::FPackageData & PackageData, unsigned int & ResultFlags, int & OutNumPushed) Line 2880
UnrealEditor-UnrealEd.dll!UCookOnTheFlyServer::PumpLoads(UE::Cook::FTickStackData & StackData, unsigned int DesiredQueueLength, int & OutNumPushed, bool & bOutBusy) Line 2814
[Inline Frame] UnrealEditor-UnrealEd.dll!IsEngineExitRequested() Line 391
UnrealEditor-UnrealEd.dll!UCookOnTheFlyServer::TickMainCookLoop(UE::Cook::FTickStackData & StackData) Line 1515
UnrealEditor-UnrealEd.dll!UCookOnTheFlyServer::TickCookByTheBook(const float TimeSlice, ECookTickFlags TickFlags) Line 1397
UnrealEditor-UnrealEd.dll!UCookCommandlet::RunCookByTheBookCook(UCookOnTheFlyServer * CookOnTheFlyServer, void * StartupOptionsAsVoid, ECookByTheBookOptions CookOptions) Line 667
UnrealEditor-UnrealEd.dll!UCookCommandlet::CookByTheBook(const TArray<ITargetPlatform *,TSizedDefaultAllocator<32>> & Platforms, TArray<FString,TSizedDefaultAllocator<32>> && ExcludedCategories) Line 628
UnrealEditor-UnrealEd.dll!UCookCommandlet::Main(const FString & CmdLineParams) Line 289
UnrealEditor.exe!FEngineLoop::PreInitPostStartupScreen(const wchar_t * CmdLine) Line 4326
[Inline Frame] UnrealEditor.exe!FEngineLoop::PreInit(const wchar_t *) Line 4633
[Inline Frame] UnrealEditor.exe!EnginePreInit(const wchar_t *) Line 44
UnrealEditor.exe!GuardedMain(const wchar_t * CmdLine) Line 153
UnrealEditor.exe!LaunchWindowsStartup(HINSTANCE__ * hInInstance, HINSTANCE__ * hPrevInstance, char * __formal, int nCmdShow, const wchar_t * CmdLine) Line 297
UnrealEditor.exe!WinMain(HINSTANCE__ * hInInstance, HINSTANCE__ * hPrevInstance, char * pCmdLine, int nCmdShow) Line 351
[External Code]	

[Attachment Removed]

This is the object recycling code running - in general object recycling is not an edge case that we expect to run on load. In addition to object recycling, we’re running object initializers that were deferred to accommodate dependency loads. Your flag adjustment may work around the problem - but make sure you only do it in WITH_EDITOR configurations, as at runtime it would be even less expected. It may be better to avoid the object recycling entirely, but that can also be problematic because load order of subobjects used on a CDO is very fragile - this is the desired load order in general:

  1. BP creates (via compile on load) or serializes its CDO export, which contains overrides from the user
  2. CreateExport() runs the native ctor to serialize instance of the BP which calls CreateDefaultSubobject()
  3. PostConstruct() runs instancing

If we allowed GetInstancedSubobject() to find/return the existing instance, it will find the one from (2) which will be missing overrides from (1)

When an issue is intermittent in cook it’s usually because of load order changes due to GC. If you want to tighten up the repro try to note the root object load and load that in an editor session or commandlet and see if the failed LoadPackage is reproducible. We’re happy to look at test cases if you can provide them.

[Attachment Removed]

Thank you for the clarifications on intended order of operation. I don’t think this particular issue was GC-related as it was happening very consistently. In spite of that, I don’t have a shareable test case due to the amount of entangled content and (unrelated) engine divergences in our game :frowning:

I did end up wrapping the proposed modification in WITH_EDITOR as you suggest, and it has held up so far (silencing build errors, and not leading to runtime issues). Thanks again!

[Attachment Removed]