Missing SpawnedActor = nullptr in UAbilityTask_SpawnActor::BeginSpawningActor

In UAbilityTask_SpawnActor::BeginSpawningActor, there is a SpawnedActor passed in by reference. Each time this function is called via the Spawn Actor Latent Blueprint node the same actor is passed back in that was previously spawned.

This generally has not caused an issue but if this ends up being called during World Teardown (which causes the first branch to not go into as ShouldBroadcastAbilityTaskDelegates return false), then the flow of code will still return true and return the previous actor from before (which in this case will be stale garbage).

bool UAbilityTask_SpawnActor::BeginSpawningActor(UGameplayAbility* OwningAbility, FGameplayAbilityTargetDataHandle TargetData, TSubclassOf<AActor> InClass, AActor*& SpawnedActor)
{
	if (Ability && Ability->GetCurrentActorInfo()->IsNetAuthority() && ShouldBroadcastAbilityTaskDelegates())
	{
		UWorld* const World = GEngine->GetWorldFromContextObject(OwningAbility, EGetWorldErrorMode::LogAndReturnNull);
		if (World)
		{
			SpawnedActor = World->SpawnActorDeferred<AActor>(InClass, FTransform::Identity, NULL, NULL, ESpawnActorCollisionHandlingMethod::AlwaysSpawn);
		}
	}
	
	if (SpawnedActor == nullptr)
	{
		if (ShouldBroadcastAbilityTaskDelegates())
		{
			DidNotSpawn.Broadcast(nullptr);
		}
		return false;
	}

	return true;
}

The fix appears to be just set SpawnedActor to nullptr at the top of the function. This is identical to how the other ability tasks that spawn actors work.

bool UAbilityTask_VisualizeTargeting::BeginSpawningActor(UGameplayAbility* OwningAbility, TSubclassOf<AGameplayAbilityTargetActor> InTargetClass, AGameplayAbilityTargetActor*& SpawnedActor)
{
	SpawnedActor = nullptr;
...

bool UAbilityTask_WaitTargetData::BeginSpawningActor(UGameplayAbility* OwningAbility, TSubclassOf<AGameplayAbilityTargetActor> InTargetClass, AGameplayAbilityTargetActor*& SpawnedActor)
{
	SpawnedActor = nullptr;

So the end result would be:

bool UAbilityTask_SpawnActor::BeginSpawningActor(UGameplayAbility* OwningAbility, FGameplayAbilityTargetDataHandle TargetData, TSubclassOf<AActor> InClass, AActor*& SpawnedActor)
{
	SpawnedActor = nullptr; //FIX

	if (Ability && Ability->GetCurrentActorInfo()->IsNetAuthority() && ShouldBroadcastAbilityTaskDelegates())
	{
		UWorld* const World = GEngine->GetWorldFromContextObject(OwningAbility, EGetWorldErrorMode::LogAndReturnNull);
		if (World)
		{
			SpawnedActor = World->SpawnActorDeferred<AActor>(InClass, FTransform::Identity, NULL, NULL, ESpawnActorCollisionHandlingMethod::AlwaysSpawn);
		}
	}
	if (SpawnedActor == nullptr)
	{
		if (ShouldBroadcastAbilityTaskDelegates())
		{
			DidNotSpawn.Broadcast(nullptr);
		}
		return false;
	}


	return true;
}

[Attachment Removed]

Steps to Reproduce

Call the Ability Task node Spawned Actor (UAbilityTask_SpawnActor) twice in a row and breakpoint at the top of each code in C++.

[Attachment Removed]

Hello [mention removed]​,

Thanks for the detailed report. I was able to reproduce the behavior you described in UE 5.7 when executing the Spawn Actor ability task node multiple times inside a ForLoop. The temporary storage used for SpawnedActor appears to be reused across iterations, which causes the previous actor reference to persist instead of being nullptr.

I will be raising a bug report for this so the team can review and integrate the fix into the engine. As you mentioned, it seems the most straightforward fix would be to set SpawnedActor to nullptr at the start of BeginSpawningActor.

I will follow up soon with more information.

Best,

Francisco

[Attachment Removed]

Hello again [mention removed]​,

I’ve raised an internal bug report so the team can review and handle the issue. You should be able to track the status of the bug in the following link: https://issues.unrealengine.com/issue/UE\-369893\. Please note that the link may take some time to become publicly visible on the tracker.

In the meantime, as you suggested, a straightforward workaround would be to explicitly set SpawnedActor = nullptr at the start of BeginSpawningActor, which matches the pattern used in other ability tasks that spawn actors.

Please let me know if you need anything else regarding this case. Otherwise, I’ll go ahead and close this ticket.

Best,

Francisco

[Attachment Removed]