Instance.Destroying firing out of order / late

Currently, as of 6/11/24, the <Instance>.Destroying event fires late, resulting in events being received out of order by game logic.

To reproduce, run the following code:

local Players = game:GetService("Players")

local function PlayerAdded(Player)	
	Player.CharacterAdded:connect(function(Character)
		warn("Player.CharacterAdded fired")
		
		Character.Destroying:connect(function()
			warn("Character.Destroying fired")
		end)
	end)
	
	Player.CharacterRemoving:connect(function(Character)
		warn("Player.CharacterRemoving fired")
	end)
end

if #Players:GetPlayers() > 0 then
	for _, Player in pairs(Players:GetPlayers()) do
		coroutine.wrap(PlayerAdded)(Player)
	end
end
Players.PlayerAdded:connect(PlayerAdded)

Observe that Player.CharacterAdded is fired and received before Character.Destrying is fired and received. This issue occurs regardless of signal behavior, even if it’s set to Immediate:

Player.CharacterAdded fired
Player.CharacterRemoving fired
Player.CharacterAdded fired
Character.Destroying fired

Expected behavior

It is expected that Instance.Destroying would not fire late, and would be received in-order like any other signal. Using the code above, this would be the expected output:

Player.CharacterAdded fired
Character.Destroying fired
Player.CharacterRemoving fired
Player.CharacterAdded fired
5 Likes

Thanks for the report! Looking for someone at Roblox to check this out.

2 Likes

This looks like expected behavior to me. The last event that should fire before an instance is destroyed is ‘Destroying’ and that is indeed what is happening here.

I do agree it’s perhaps a little strange that CharacterAdded fires in between Removing and Destroying however, I believe that’s because we defer destruction of the character. Let me discuss this with another member of staff as I think they’ll be able to fill in the details here.

1 Like

Just confirmed, this is intentional. We defer the destruction until after the new character has been assigned to assure it’s not destroyed too early.

What’s the reasoning for this? What would happen if a character was destroyed before the new one is done loading?

It’s for backwards compatibility in cases where you still want to use the character after it’s been removed. This is particularly relevant when using LoadCharacter as this leads to the CharacterAdded event be triggered. We don’t want the old character to have been destroyed at that point.

2 Likes

Could this be documented under LoadCharacter() and its associated events? I was not aware of this behavior and it caused a fair amount of debugging time on our end.

1 Like