Class UBlueprintAsyncActionBase provides methods RegisterWithGameInstance() and SetReadyToDestroy(), which respectively register and unregister the object with the game instance.
Derived class UCancellableAsyncAction, in turn, provides a method IsRegistered(), which checks if the internal weak pointer "RegisteredWithGameInstance" is valid. In the current implementation, however, this internal pointer is set by RegisterWithGameInstance() and not cleared by SetReadyToDestroy(), which leads to an incorrect "true" result.

As a consequence, method UCancellableAsyncAction::ShouldBroadcastDelegates() also returns true even after Cancel() or SetReadyToDestroy() has been called on the action, allowing completion delegates to still be called in unexpected situations. This can be observed, for example, when attempting to cancel "UAsyncAction_CommonUserInitialize" in the Lyra project using "CancelUserInitialization()".

Interestingly, class UAbilityAsync deals with this by checking its own internal weak pointer to a UAbilitySystemComponent on its overload of ShouldBroadcastDelegates(). This pointer is properly set by subclasses and reset in the shared Cancel()/EndAction() methods. But it probably should not be mandatory that all subclasses of UCancellableAsyncAction implement a similar check on top of the existing one, since they might not have any extra natural condition to check.

This issue is also discussed on the linked UDN case (

Steps to Reproduce

Have Comments or More Details?

There's no existing public thread on this issue, so head over to Questions & Answers just mention UE-218820 in the post.

Login to Vote

ComponentUE - Gameplay - Blueprint
Affects Versions5.4
Target Fix5.5
CreatedJun 26, 2024
UpdatedJun 28, 2024
View Jira Issue