Nativized project crashes at startup with random, undebuggable assertion errors

Engine versions: 4.22 and 4.24

Background: I’m currently working on a project where I started out with Blueprints, and I’ve been working with 99% blueprints ever since. Recently, I decided to try out the “Nativization” setting, and boy have that been problematic. It works if I select nothing to nativize (apart from an Enum-Switch case not working) but when I use nativization like it’s supposed to, I’m having so many issues I don’t really know what to name this thread, it’s just as if nothing works and I feel helpless. The error messages give me nothing to go on and all hail from a single file throwing assertion errors: Runtime\CoreUObject\Private\Serialization/AsyncLoading.h .

What: The program I compile crashes at startup when using Nativization. Compiling gives no error messages.

Is there an error message?: Yes, but there is a built-in assertion throwing the error. It gives me no indication of **why **it crashes or where the error has occured that caused the error to be raised. Compilation shows no error other than a warning that a referenced directory missing, which it does when compiling for blueprints as well, and in that case it works fine. “Fixing” this transforms it into a more dangerous warning that there are now two competing directories for the plugin. I won’t touch that further.

What I have tried: Countless configurations. What works is if I nativize the project but don’t include any files, or only files that don’t reference anything else. When including the blueprints I really want to nativize, the only difference seems to happen between Inclusive/Exclusive nativization.

Exclusive nativization: When using exclusive nativization, I get the crash with the error “Async loading event graph contained a cycle, see above.”. It was suggested to me that this is because of using “Exclusive nativization” on only a few blueprints, and the cross-referencing between C++ and blueprints caused a disallowed cycle. Thus I started trying “Inclusive nativization”. No googled threads solved the issue. In 4.24, It says “Assertion failed: !bCheckAdded || NodeRef.bAddedToGraph”. Full logs at bottom.

Inclusive nativization: Now, the project still compiles fine, but the compile time goes from 1 hour to 7 hours, making it hard to debug. When starting the program, I now get another error: Assertion failed: “RecursionNotAllowed.Increment() == 1”. That is without changing anything, which doesn’t make sense.

Error logs:
4.22, Exclusive nativization (Async loading event graph contained a cycle, see above.)
4.22, Inclusive nativization (Assertion failed: RecursionNotAllowed.Increment() == 1)
4.22, Another startup map (Error: Cycle Node in log) (New!)
4.24, Exclusive nativization (Assertion failed: !bCheckAdded || NodeRef.bAddedToGraph)
4.24, Inclusive nativization is exactly the same as 4.22
There is no difference between these versions and the 4.24 project is directly upgraded from 4.22. Some nodes were disconnected in this process due to an engine bug and were not reconnected before compiling.

My project is 99% blueprints and compiles fine without nativization, so I believe the C++ code is fine. I do not claim that my programming is perfect, I had a ton of nodes looking like this and a user suggested I remove it, but it has not solved either of the errors I’m getting. Where do I start with debugging this? I have tried my best to reproduce it either by removing huge chunks of the project and compiling these separately or by starting a new project and trying to make it crash, and I can’t do it. It’s only in this project, and I can’t figure out what’s gone wrong.

Where to I start debugging this? What is the point of the assertions if they don’t tell what is wrong? I have no clues to go by and I need desperate help.

I’ve made what seems to be progress? When using Exclusive nativization in 4.24 and selecting the blueprints I want, as well as changing the startup map, I can generate actual names of assets associated with errors like so:
[2020.01.09-09.42.48:040] 0]LogStreaming: Error: Cycle Node /Game/Classes/Mission/DiscoverTileObjective 2 Export_StartIO /Game/Classes/Mission/DiscoverTileObjective/DiscoverTileObjective_C/

However, I can’t figure out what it thinks is wrong with these assets? I have checked them multiple times and there is nothing that can constitute a “cycle”, so is it a relationship between these assets causing a dependency cycle that doesn’t work in C++? The Blueprint in question is a child blueprint that contains very little information and no executing functionality so I highly doubt there’s an error in it. This still doesn’t make sense as these assets are still not executed in the Main Menu map. They have no reason to cause a crash.

There is also something really fishy here in this line:
[2020.01.09-09.42.48:039] 0]LogStreaming: Error: Cycle Node /Game/OwnFuncsAndStructs/Actions/MinerArray -1533 Import_Create /Game/Classes/Mission/DiscoverTileObjective/Default__DiscoverTileObjective_C/
as “MinerArray” and “DiscoverTileObjective” **never **references each other. Why are they printed together?

The keywords that keep popping up are “Import”, “Export” and “Serialize”, as well as “Async loading”. I’ve disabled “Async Loading Thread Enabled” as it is not there by Default, and I’m keeping EDL enabled. Disabling EDL in 4.22 and 4.24 is impossible for my project as there are features that require EDL, but if I remove those, the entire project compiles, but the game stops working

Bumping this as no progress is being made towards solving or explaining the core issue

I’ve managed to identify some classes that actually can be exclusively nativized (such as a vehicle class) but nativizing these makes them not move anymore and doesn’t improve performance. That means it’s just a worse option. Nativizing in general (i.e. even with zero assets nativized) breaks some functionality such as a certain Enum-Switch case I have. Since some Blueprints can be nativized, I suppose there is something in these other ones that can’t be nativized, such as some cross-references? One example is how basically every blueprint has the GameMode as a variable to quickly fetch it, perhaps things like these are not possible

I’ve actually made some progress with this: It turns out that according to whatever documentation, in this case a random benefactor off of Reddit, Structs and Enumerators have to be in C++ of nativized assets. After nativizing all enumerators of a BP class I had, it gave some clearer errors regarding some other non-nativized BP assets. I nativized those as well, and then I compiled without problems and the program actually started.

So while this was probably the issue, the resulting program is god-awful broken. All “Move To” navigation commands have turned into “Simple move to”, some collision settings are reset for no reason at all, and a multitude of functions return True despite giving an error message that is sent right before it returns False. I have no idea why this is but I’ll keep trying to find the reason it’s broken.

Current theory is that the nativization in itself is broken as hell though.

Hi,

how did you do that? Enumerators used by Blueprints do not have nativization option. Do I need to create them separately in C++? Will that do the trick?
Thank you for your research!

I completely missed this, but yes, you have to create a new, completely empty C++ class. To create a global enumerator that Blueprints can use, you do the following in the Header (.h) file:

#pragma once
#include “EnumLibrary.generated.h”

UENUM(BlueprintType)
enum EMyEnumeration
{
EnumThing1 UMETA(DisplayName = “The first thing”),
EnumThing2 UMETA(DisplayName = “The second thing”),
}

The .cpp file is empty apart from including the Header file. If this doesn’t compile, the .generated file might be missing. If so, compile the header with no enumerations first ( use /* and */ to comment everything out) and then compile again with the enumerations when it succeeds.

For Structs it is very similar:

#pragma once
#include “CoreMinimal.h”
#include “StructLibrary.generated.h”

USTRUCT(BlueprintType)
struct FTestStruct
{
GENERATED_BODY() public: // A standard macro

UPROPERTY(BlueprintReadWrite, EditAnywhere) // This is necessary to make sure Blueprints can see what's going on
int SampleInt;

//Constructor, called when you create the Struct form default. In this case, my default int is always 5. Necessary for use in Blueprints.
FTestStruct()
{
    SampleInt = 5;
}
//Destructor, executes when it is destroyed. Good to have.
~FVectorArray()
{
}
friend uint32 GetTypeHash(const F4DIntVec& Other) // Required if you want to use the struct in Maps and Sets.
{
    return GetTypeHash(Other.IntX * Other.IntY * Other.IntZ * Other.IntW);
}

};

However I should stress that despite this, I have not properly nativized the game without issues. I decided instead to rewrite any demanding parts or functions in C++, as that would give a smoother experience for me and not have those incredibly long convert+compile times. I therefore do not know at this point if this is the “final issue” to solve the nativization issues. I would suppose the issues I had when I did successfully nativize, such as a botched AI and messed-up collision settings for vehicles, would still remain.

Lesson learned: When you start a game project with potentially demanding components, always build the core of it in C++. Declare all functions and variables in C++ classes, then override in Blueprints if you must.

Thank you for your reply and posting your findings!

“…Lesson learned: When you start a game project with potentially demanding components, always build the core of it in C++. Declare all functions and variables in C++ classes, then override in Blueprints if you must…”

This completely ruins the experience of creating with Blueprint and hoping to compile it in C++ with nativization.

If your expectation is “I can check a checkbox, and magically get all the benefits,” then yes, that experience was “ruined.”
But that’s not actually an experience anyone expected to deliver, as far as I can tell.
You’re not going to be able to compile to C++ without a little bit of elbow grease. That’s just how the world works.

I’m full of elbow grease and I don’t like being parked with “one button push” people. It is unfair …
All videos show that feature like that. Check Incluse for small project. And exclude your BP cases by cases for other ones.

If we have to declare variables in c ++, it is because this nativalization is an incomplete functionality.
This should not be a conversion failure.
Or else, we should be warned during the process. And not discorver at the end, that half of your project doesn’t work.
This need a compatibility note, with process and warnings.

I totally agree with you. You are %100 right in this. Epic Games should address this in the manner you mentioned.

Thank you so much for sharing your findings!

This is an engine bug, it will be fixed soon (hopefully)