CharacterAdded event is fired twice when character loads

When a player’s character spawns, or LoadCharacter() is invoked, the CharacterAdded event is being fired twice for some reason.

At first, I thought maybe it was my code doing something, so I decided to isolate everything and test with this simple code:

LocalScript in StarterPlayerScripts

local Players = game:GetService("Players")
local player = Players.LocalPlayer

local function _HandleCharacterAdded(character: Model): ()
	print("Character Added")
end

player.CharacterAdded:Connect(_HandleCharacterAdded)

And the results are…
image
The function was invoked two times, leading to two prints, meaning the event got fired twice.

I tried everything to fix this, checking other forum posts on this topic, and reading Roblox Docs, but unable to find a solution.

Is this just a blatant engine bug? Is there a bandaid fix for this? How can I make it not fire twice, or at least catch the second firing attempt and just void it?

Hello. I inserted your script into studio and it only fired once for me.
image
Maybe try using find all/replace all tool and check if you didnt put same script somewhere else

You mentioned you call LoadCharacter(), is players.CharacterAutoLoads = false?

If setting this property via script, you will need to make sure it is done very early before any players get chance to have their character loaded automatically. (In studio this can be pretty quick).
If it is set to true, then chances are the load character call is simply reloading the character.

Can you put this code in for your print statement and let me know what outputs?

local function _HandleCharacterAdded(character: Model): ()
    print("Character added. Script:", script:GetFullName())
end

Also:

  • It’s a LocalScript, not Script with RunContext set to Client, right?
  • Does this only happen in your place?
  • There’s no behaviour that could be causing the script to run twice? No rapid disabling or enabling it? No re-parenting?
  • Does this happen in Roblox as well as Studio?

As a workaround, you can debounce the function:

local db = false

local function _HandleCharacterAdded(character: Model): ()
    if (db) then return nil end
    db = true

    print("Character added!")

    task.wait(0.5)
    db = false
end

I encountered a very inconsistent issue like this in the past and made a bug report on it, but I’m yet to find a solid repro for it.

I tested out a little more and found out this:

  • A LocalScript in StarterPlayerScripts works fine and the event is fired once in my place.
  • What I haven’t said in my post, I probably should’ve, I am using module loader or Single-Script Architecture in my place. I connect to the CharacterAdded event in the module script when the module loads (the module is being required for the first time)

I apologize for any confusion and for not saying this before, as well as possibly misleading.

I tried out with new module script, I usually structure my modules like this:

Code
-------------------------------
-- == SERVICES & REQUIRES == --
-------------------------------
local Players = game:GetService("Players")

-------------------------------
-- == CONSTANTS & TYPES == --
-------------------------------

-------------------------------
-- == VARIABLES == --
-------------------------------
local Test = {}

local player = Players.LocalPlayer

-------------------------------
-- == PRIVATE FUNCTIONS == --
-------------------------------

local function _HandleCharacterAdded(character: Model): ()
	print("Character Added")
end

-------------------------------
-- == PUBLIC FUNCTIONS == --
-------------------------------

-------------------------------
-- == HANDLERS == --
-------------------------------
player.CharacterAdded:Connect(_HandleCharacterAdded)

-------------------------------
-- == MAIN == --
-------------------------------
return Test

In this case, the event is fired once:

I guess there is something with the original module because it catches CharacterAdded twice like it is being fired twice:

Here is the code of the original module.

Code
--!nonstrict

debug.setmemorycategory("ARC Framework - StaminaHandler")

-------------------------------
-- == SERVICES & REQUIRES == --
-------------------------------
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local Signal = require(ReplicatedStorage.Modules.Utilities.Signal)
local StaminaClass = require(script.StaminaClass)

-------------------------------
-- == CONSTANTS & TYPES == --
-------------------------------

-------------------------------
-- == VARIABLES == --
-------------------------------
local StaminaHandler = {}

local staminaMap: { [Humanoid]: StaminaClass.Stamina } = {}

local player = Players.LocalPlayer

StaminaHandler.StaminaCreated = Signal.new()

-------------------------------
-- == PRIVATE FUNCTIONS == --
-------------------------------

local function _HandleCharacterAdded(character: Model): ()
	local humanoid = character:FindFirstChildOfClass("Humanoid")

	local stamina = StaminaClass.new(humanoid)

	staminaMap[humanoid] = stamina

	StaminaHandler.StaminaCreated:Fire(stamina)
end

local function _HandleCharacterRemoving(character: Model): ()
	local humanoid = character:FindFirstChildOfClass("Humanoid")

	staminaMap[humanoid]:Destroy()
	staminaMap[humanoid] = nil
end

-------------------------------
-- == PUBLIC FUNCTIONS == --
-------------------------------

function StaminaHandler:GetStamina(humanoid: Humanoid): StaminaClass.Stamina?
	return staminaMap[humanoid]
end

-------------------------------
-- == HANDLERS == --
-------------------------------
player.CharacterAdded:Connect(_HandleCharacterAdded)
player.CharacterRemoving:Connect(_HandleCharacterRemoving)

-------------------------------
-- == MAIN == --
-------------------------------
return StaminaHandler

For context, it is supposed to be a player stamina handler. It checks if the player’s character is added and creates a stamina class for that player’s character humanoid.

I will appreciate for any help or assistance.

If anyone wonders what module loader I use, it is Crusherfire Module Loader:
Crusherfire Module Loader.rbxm (10.3 KB)

So I found out it was doing that because I had my StaminaHandler module nested in folders.
Un-nesting the module script, fixes the issue, but, I also tried with fresh module scripts and in nested folders they would work fine, CharacterAdded is caught once, not twice.
So it seems like my StaminaHandler module is being funky and does not want to sit in nested folders, because then it will catch the CharacterAdded event twice. Strange… Maybe it is because of some modules that I require and use? There’s a lot to figure out.