We have some custom code that creates UObject instances from JSON using FJsonObjectConverter::JsonObjectToUStruct. This happens when the asset registry has finished loading files.
IAssetRegistry& assetRegistry = IAssetRegistry::GetChecked();
if (assetRegistry.IsLoadingAssets())
{
assetRegistry.OnFilesLoaded().AddRaw(this, &FCustomModule::Initialize);
}
This works fine in Unreal 5.4. We are upgrading to 5.6 and getting a lot of asserts when it initializes, specifically on this line:
void UObject::ConditionalPostLoad()
{
...
ensureAlwaysMsgf((GetLoaderType() != ELoaderType::ZenLoader) || !HasAnyFlags(RF_NeedLoad), TEXT("Object '%s' does not have RF_NeedLoad cleared in PostLoad!"), *GetFullName());
The assert isn’t happening on the object being loaded by the JSON. Rather, it seems the issue is this:
- Calling StaticLoadObject is triggering FlushAsyncLoading(…)
- That flush triggers a Blueprint compilation queue flush
- It tries to duplicate an object that has the RF_NeedLoad flag still
- Asserts when it tries to call PostLoad on that object
Any ideas on how we can fix this or work around it?
Here is the compressed callstack (too many characters for the full one):
`UObject::ConditionalPostLoad'::`2'::<lambda_1>::operator(...)
UObject::ConditionalPostLoad()
StaticDuplicateObjectEx(...)
StaticDuplicateObject(...)
FBlueprintCompileReinstancer::MoveCDOToNewClass(...)
FBlueprintCompileReinstancer::FBlueprintCompileReinstancer(...)
FBlueprintCompilationManagerImpl::FlushCompilationQueueImpl(...)
FBlueprintCompilationManager::FlushCompilationQueue(...)
FScopedClassDependencyGather::~FScopedClassDependencyGather()
FLinkerLoad::CreateExport(...)
FLinkerLoad::IndexToObject(...)
FLinkerLoad::CreateExport(...)
FLinkerLoad::IndexToObject(...)
FLinkerLoad::CreateExport(...)
FAsyncPackage2::CreateLinkerLoadExports(...)
FAsyncPackage2::Event_CreateLinkerLoadExports(...)
FEventLoadNode2::Execute(...)
FAsyncLoadEventQueue2::ExecuteSyncLoadEvents(...)
FAsyncLoadingThread2::ProcessAsyncLoadingFromGameThread(...)
[Inlined] FAsyncLoadingThread2::TickAsyncThreadFromGameThread(...)
FAsyncLoadingThread2::TickAsyncLoadingFromGameThread(...)
FAsyncLoadingThread2::FlushLoading(...)
FlushAsyncLoading(...)
FlushAsyncLoading(...)
LoadPackageInternal(...)
LoadPackage(...)
LoadPackage(...)
ResolveName2(...)
StaticLoadObjectInternal(...)
StaticLoadObject(...)
FObjectPropertyBase::FindImportedObject(...)
[Inlined] IsObjectHandleNull(...)
[Inlined] FObjectPtr::operator bool()
[Inlined] ObjectPtr_Private::IsObjectPtrNull(...)
[Inlined] TObjectPtr::operator==(...)
FObjectPropertyBase::ParseObjectPropertyValue(...)
FObjectPropertyBase::ImportText_Internal(...)
FObjectProperty::ImportText_Internal(...)
FProperty::ImportText_Direct(...)
`anonymous namespace'::ConvertScalarJsonValueToFPropertyWithContainer(...)
`anonymous namespace'::JsonValueToFPropertyWithContainer(...)
`anonymous namespace'::JsonAttributesToUStructWithContainer(...)
`anonymous namespace'::ConvertScalarJsonValueToFPropertyWithContainer(...)
`anonymous namespace'::JsonValueToFPropertyWithContainer(...)
`anonymous namespace'::ConvertScalarJsonValueToFPropertyWithContainer(...)
`anonymous namespace'::JsonValueToFPropertyWithContainer(...)
`anonymous namespace'::JsonAttributesToUStructWithContainer(...)
[Inlined] FJsonObjectConverter::JsonAttributesToUStruct(...)
FJsonObjectConverter::JsonObjectToUStruct(...)
FJsonDataObjectConverter::JsonObjectToStruct(...)
FJsonDataObjectConverter::JsonObjectToUObject(...)
FJsonDataObjectReader::SerializePropertyValues(...)
FDataObjectReader::SerializeObjectData(...)
UDataObject::Create(...)
FCustomModule::Initialize()
TBaseRawMethodDelegateInstance::ExecuteIfSafe()
[Inlined] TMulticastDelegateBase::Broadcast()
TMulticastDelegate::Broadcast() DelegateSignatureImpl.inl:1080
UAssetRegistryImpl::Broadcast(...)
UAssetRegistryImpl::Tick(...)
[Inlined] FAssetRegistryModule::TickAssetRegistry(...)
UEditorEngine::Tick(...)
UUnrealEdEngine::Tick(...)
FEngineLoop::Tick()
[Inlined] EngineTick()
GuardedMain(...)
LaunchWindowsStartup(...)
WinMain(...)