Potential risks with the BindWidget meta tag

While the BindWidget metatag is useful in allowing the native environment to access widgets directly in the panel, I would strongly advise against using it in an actual production environment for a few reasons:

  1. You run a serious risk of introducing a cyclic dependency between the native code and the Blueprint, whereby the native code can’t compile correctly because it relies on assets in the widget Blueprint to do so, but the widget blueprint simultaneously depends on the native code. The dangerous thing about cyclic dependencies (also called circular references) is that they won’t always fail because they’re going to be dependent on compile order, and will rarely if ever fail in the editor, so you can wind up with something that looks like it’s working but will actually fail intermittently in packaged builds.

If you hit this assertion in a packaged build (UE5 EA 2):
Assertion failed: Level == 1 [File:D:/build/++UE5/Sync/Engine/Source/Runtime/CoreUObject/Private/Serialization/AsyncLoading2.cpp] [Line: 4017]
That’s what’s happening.
If you look at the struct FScopedLoadRecursionVerifier at that location, you’ll see that it’s looking for these sorts of recursive definitions and asserting out.
I don’t know whether UE4 does a similar check but this jumped up and bit me in UE5.

In my case this made my project intermittently unable to run its packaged builds - I would make a build that succeeded, make a change, try again and it would fail, then revert back to the previously working build and it would still fail, because the issue wasn’t explicitly in code but in the compile order.

I was able to resolve this by eliminating the use of BindWidget tags an instead making BlueprintImplementableEvent calls from native to the widget blueprint to pass data, and handling all the user display within the context of the Blueprint.

  1. The other reason why you don’t want to use these BindWidget tags is you’re creating invisible failure points for your UI artist. This isn’t so much of a problem if you’re designing a game alone, but if you’re working on a team, you don’t want to make choices in the native binaries that unnecessarily constrict the artist’s work downstream, since they may not have visibility into the source code. When you pass the class to the UI artist, they have no way of knowing that if they rename an element in the UI, their class could suddenly fail to compile. Better to use BlueprintImplementableEvents to let them see that you’re giving them an event with data, and allow them to depict that data to the player however they’d like.

I’ve found in practice that it’s a good idea to keep a meaningful separation between the work you do in UI native classes and in their Blueprint classes. It seems to work best if you think of the native environment as the place to do all of your functional logic - hold the data there, generate events, etc., and use the Blueprint environment solely to draw the results of those events and to accept input from the player. i.e., C++ == the logic layer, UMG Blueprint == the UI painting layer.

Anyway, just wanted to share this since it made for a gnarly day of debugging to track down.
(UDN thread here: [UE5.0EA2] Packaged build crashes in AsyncLoading2.cpp, Line 4017 (unrealengine.com) )

Thank btw for an absolutely fantastic course!

1 Like

Thanks for sharing. I find this interesting because it if’s something you shouldn’t do, why is the feature available? I understand if an artist redesigned the UMG and didn’t name the widgets correctly, the system would start breaking - that makes sense. I’ve never encountered issues before using it myself however and the system seems to work well.

I see you mention UE5 so I do wonder if the issues you saw were specific to that rather than the method being used in the course.

It’s totally possible that UE5 may be managing its compile order or handling multithreaded compilation differently from the way things were done in UE4 - I did notice that even during the earlier tutorial steps, the UMG userwidgets with BindWidget tags would often fail to recompile in-editor after modification, but would compile fine once the editor was shut down and recompiled from Visual Studio, and I didn’t see any other users mentioning this, which suggests it may be specific to UE5.

In terms of why the feature is available, I don’t think it would be fair to say that the feature itself is inherently dangerous, but that it can introduce risky patterns. I can see where it could definitely fill a need, for instance if you were refactoring an old Slate UI to use UMG and wanted to keep it functional midway through the refactor, so it makes sense to have it around. I’m betting it’s not heavily tested since it doesn’t appear to be used anywhere in Epic’s own code and isn’t a super-well-known feature in the developer community. Again, totally possible that this is safe in UE4, and might be intended to be safe in UE5 and just be in a shaky state right now. I’ve submitted this to Epic as Case # 00357125 in case it’s a bug they should know about.

1 Like

Just curious but overall, how are you finding the course so far with UE5? anything major we should be aware of?