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!

4 Likes

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?

Hi, Thank you for PinPointing this Issue which was impossible to solve. I have one question, Do you recommend any course, tutorial, or documentation to guide us in solving the issue?

Assertion failed: Level == 1 [File:D:/build/++UE5/Sync/Engine/Source/Runtime/CoreUObject/Private/Serialization/AsyncLoading2.cpp] [Line: 4495]

I would agree with you !

Start by turning off live coding, exit the editor and delete the binaries folder from the project directory. Then launch again. You can often resolve these kinds of issues with this simple step. not always. Also, it is better to raise unrelated items in a new thread.

1 Like

I know you asked this over a year ago, but I’ve been going through this course with UE5.0.3 and haven’t had much issues beyond some minor UI changes. Nothing in the course has been impossible for me to do yet.

I also haven’t run into the issue OP described yet, but I’ll keep my eye out for it.

His issue is related to the live coding. It doesn’t seem to happen all the time but sometimes the C++ doesn’t build correctly. I see it all the time here in this course and in the beginners course too.

Great points here! However I’m not quite getting where the dependency cycle occurs. I would imagine the build dependency to look like BP_MyWidget → MyWidget → UUserWidget.

What part of compilation makes the native code reference the BP?

It is actually a combination of atrributes in your own class, UPROPERTY, UFUNCTION and the likes, indicating variables and methods are exposed along with the architecture on the engine which is designed to import your code natively, basically extending the engine with new classes. The Blueprint can then consume these new classes.

It’s quite an impressive setup.

So, the engine gives you base types at varying degrees of specialty such as actor, pawn and character as an example, and you can inherit these classes to make your own.

The actual blueprint, be it widget or animation or regular blueprint are dependent on what they inherit from and whatever they inherit from and so on.

I’m not sure it this clarifies it much but I hope it helps.

1 Like

I have often read and heard that it was a “potential problem” to use BindWidget. However, I never got any concrete examples or reasons to explain this.

I decided to ask the Chat AI bot and it came up with this answer, supporting the use of BindWidget over manual UButton lookup:

is this safer than BindWidget? I am told BindWidget can lead to cyclic references and also doesn’t always work in Production Level builds

ChatGPT

Using BindWidget is generally considered safer and more efficient than manually looking up widgets using GetWidgetFromName or similar methods. Here’s why:

  1. Compile-time validation: With BindWidget , the widget references are resolved at compile-time, which means that any errors, such as typos in widget names, missing or mismatched types, will be caught during compilation. This provides early detection of issues and helps ensure that the widget references are valid and correctly set up.
  2. Automatic binding: BindWidget automatically binds the widget references to the corresponding widget instances in the Blueprint or UI hierarchy. This simplifies the setup process and reduces the chances of making mistakes or forgetting to bind the widgets manually.
  3. Efficient performance: BindWidget assigns the widget references directly to the class member variables, allowing for efficient access and usage. In contrast, using GetWidgetFromName requires a runtime lookup by name, which can be slower and less efficient, especially when performed frequently.

Regarding the concerns you mentioned:

  • Cyclic references : Cyclic references can occur when you have circular dependencies between objects, such as parent-child relationships. While it is possible to introduce cyclic references with BindWidget if not used carefully, it’s not inherently caused by BindWidget itself. It’s more about how you structure and manage your objects and their relationships. By understanding and managing your object lifecycles properly, you can avoid cyclic reference issues regardless of whether you use BindWidget or manual widget lookup.
  • Production-level builds : BindWidget should work fine in production-level builds if used correctly. However, if there are issues, they are more likely related to other factors, such as incorrect usage, missing dependencies, or optimization settings. It’s always a good practice to thoroughly test your game in different build configurations, including production builds, to ensure that the widgets and their bindings work as expected.

In summary, BindWidget is generally recommended for its compile-time validation, automatic binding, and performance benefits. However, it’s important to understand how to properly manage object lifecycles and avoid circular dependencies to prevent any potential issues, regardless of the method you choose for widget reference handling.

Always take anything that ChatGPT says with a pinch of salt. It looks at Internet resources to produce results but cannot differentiate between the good and the bad although it probably has some weighting system to try and pick favourable responses.

IMO, it is a tool that enables plaugerism. There’s a reason why some companies have outright banned its use for business purposes. (I speak from experience here, not that I would consider using it myself)

That is a separate argument which I don’t disagree with. But it seems it gives an informed summary of the facts in this case. Can anyone else add to (or contradict) what it’s saying here about BindWidget? (ie. what better alternatives are there

I think the main issue with the bind was it used names as strings to bind, I forget what exactly his point was but I do remember disagreeing. I see no issues with BindWidget at all. There is always going to be someone arguing that a given feature shouldn’t be done in a particular way.

The beauty of programming is that you can try these things out and pick your own solution.

1 Like

Yes but the only alternative I could find (not that I spent hours searching for it), was manual lookup, which also required a string to specify the names of the buttons.

In fact, bindwidget didn’t need any string, but it was strange (and first time I personally encountered this), it requires the variable name to match that of the widget component it is binding with in the WBP.

Personally I think I prefer BindWidget (based on the fact that there was no need to type a string anywhere, and the fact that the WBP picks up the changes once compiled correctly with the correct names and it just seems to work great.

With that said, I did have some times after numerous changes to code + WBP that I needed to “Clean” project files and then “Reload Asset” in the editor to remove some very weird errors (and I guess this is the reason that people advised against it).

The errors were happening at runtime only, and it basically said “Cannot find WBP you are referring to”. But the steps I mentioned seemed to fix it for me.

Privacy & Terms