You should use both. The character is assigned to Player.Character before its actual appearance is loaded — so the appearance (basically everything within the character, including the root part) is not immediately added.
You can use CharacterAppearanceLoaded (on the server, this isn’t an option for localscripts) or wait for the root part to be loaded when the character is added.
local char = game.Players.LocalPlayer.CharacterAdded:Wait()
local hrp = char:WaitForChild("HumanoidRootPart")
--// server scripts only
local char = player.CharacterAppearanceLoaded:Wait()
local hrp = char.HumanoidRootPart
local player = game.Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
character.ChildAdded:Connect(function() -- When something gets parented to the character, it will fire this function
local success, errormessage = pcall(function() -- If the the object that got added to the player isn't a HumanoidRootPart, no error will appear in the output
character:FindFirstChild("HumanoidRootPart") -- checks if a HumanoidRootPart was added to the character
Maybe you were calling the HumanoidRootPart before it was even added to the character. Replace the code that you used to get the HumanoidRootPart with this code instead.