"attempt to index nil with 'FindFirstChild'"

Recently, I started work on attempting to make a tower defense game, I’m relatively new to scripting and attempted to work on a tower script


As shown in the video, sometimes my towers will have the error
“ServerScriptService.NewTowerScript:52: attempt to index nil with ‘FindFirstChild’”
This results in the tower no longer firing and just standing blank.
I’ve attempted rewriting it but it came out to the same issue. If someone could explain to me why this is happening that would be great. Thanks :slight_smile:

local TowersFolder = game.Workspace.Towers
local RunService = game:GetService("RunService")
local Map = game.Workspace.Map
local Path = Map.Path
local Destination = Map:FindFirstChild("Path"):FindFirstChild("HomeBase"):FindFirstChild("Destination")

local function Attack(enemy, distance, tower)
	if tower:FindFirstChild("TowerInfo"):FindFirstChild("CooldownActive").Value == false then
		print(enemy.Name)
		if enemy.Parent == game.Workspace.Enemies then
			if enemy:FindFirstChild("EnemyInfo"):FindFirstChild("Health") then
				local EnemyHealth = enemy:FindFirstChild("EnemyInfo"):FindFirstChild("Health")
				if EnemyHealth.Value > 0 then
					local TowerInfo = tower:WaitForChild("TowerInfo")
					local CooldownActive = TowerInfo:FindFirstChild("CooldownActive")
					local Enemies = game.Workspace.Enemies
					local Damage = TowerInfo:FindFirstChild("Damage")
					local Cooldown = TowerInfo:FindFirstChild("Cooldown")
					local Range = TowerInfo:FindFirstChild("Range")
					local HRP = tower:FindFirstChild("HumanoidRootPart")
					local MiddlePosition = Path:FindFirstChild("HomeBase"):FindFirstChild("Destination").Position
					local EnemyPosition = enemy:WaitForChild("HumanoidRootPart")
					local PrimaryPart = tower.PrimaryPart
					PrimaryPart.CFrame = CFrame.new(PrimaryPart.Position, Vector3.new(EnemyPosition.Position.x, PrimaryPart.Position.y, EnemyPosition.Position.z))
					local EnemyInfo = enemy:FindFirstChild("EnemyInfo")
					local EnemyHealth = EnemyInfo:FindFirstChild("Health")
					EnemyHealth.Value = EnemyHealth.Value - Damage.Value
					CooldownActive.Value = true
					print(tower.Name.."Fired")
					wait(Cooldown.Value)
					TowerInfo:FindFirstChild("CooldownActive").Value = false
					end
				end
			end
	end
	end
	

local function ActiveScript(tower)
	local TowerInfo = tower:WaitForChild("TowerInfo")
	local Enemies = game.Workspace.Enemies
	local Damage = TowerInfo:FindFirstChild("Damage")
	local Cooldown = TowerInfo:FindFirstChild("Cooldown")
	local Range = TowerInfo:FindFirstChild("Range")
	local PrimeTarget = math.huge
	local PrimeDistance = math.huge
	while wait(0.1) do
		if TowerInfo:FindFirstChild("CooldownActive").Value == false then
			local Root = tower:FindFirstChild("HumanoidRootPart")
			for i, enemy in pairs(Enemies:GetChildren()) do
				if enemy ~= nil then
				local Health = enemy:FindFirstChild("EnemyInfo"):FindFirstChild("Health")
				if Health.Value > 0 then
				if enemy:FindFirstChild("Humanoid") then
				if enemy:FindFirstChild("HumanoidRootPart") then
							local EnemyRoot = enemy:FindFirstChild("HumanoidRootPart")
							local EnemyPos = EnemyRoot.Position
							local EnemyPosition = Vector3.new(EnemyPos.X, Root.Position.Y, EnemyPos.Z)
							local EnemyMag = (Root.Position - EnemyPosition).Magnitude
							if EnemyMag < Range.Value then
								local EnemyFromBase = Vector3.new(EnemyPos.X, Destination.Position.Y, EnemyPos.Z)
								local DistanceFromBase = (Destination.Position - EnemyFromBase).Magnitude
								if DistanceFromBase < PrimeDistance then
								PrimeTarget = enemy
								PrimeDistance = DistanceFromBase
								end
								Attack(PrimeTarget, PrimeDistance, tower)
							end
							end
							end
					end
				end
			end
		end
	end
	end

while wait() do
	for i, tower in pairs(TowersFolder:GetChildren()) do
		if tower:FindFirstChild("Humanoid") then
			if TowersFolder:FindFirstChild(tower.Name) then
				local TowerInfo = tower:FindFirstChild("TowerInfo")
				local Active = TowerInfo:FindFirstChild("Active")
				if Active.Value == false then
					Active.Value = true
					spawn(function()
						print("Activated: ".. tower.Name)
						ActiveScript(tower)
					end)
				end
			end
		end
	end
end

Do you mind sending the code that performs the error?
The 55th Line. (Sorry I don’t feel like counting each individual line)

if enemy:FindFirstChild("HumanoidRootPart") then
if enemy:FindFirstChild("HumanoidRootPart") ~= nil then

Ah sorry reread the video the error actually occurs at line 52 which is:
local Health = enemy:FindFirstChild(“EnemyInfo”):FindFirstChild(“Health”)

You’re all good lol.
Try :

if enemy:FindFirstChild(“EnemyInfo”):FindFirstChild(“Health”) ~= nil then
local Health = enemy:FindFirstChild(“EnemyInfo”):FindFirstChild(“Health”)
end

image
The new line seems to get errors now

Thats odd, try this.

game:GetService.Heartbeat:Connect(Function()
wait()
if enemy:FindFirstChild(“EnemyInfo”):FindFirstChild(“Health”) ~= nil then
local Health = enemy:FindFirstChild(“EnemyInfo”):FindFirstChild(“Health”)
else
local Health = nil
end
end)

The same error seems to occur each time.
I noticed that the same error only occurs if multiple towers are attacking the same unit at once, maybe (because when the enemy has 0 health it gets :destroyed) it bugs out all the other towers but I can’t figure out how to fix it.

Try this really quickly, I think I messed up.
game:GetService.Heartbeat:Connect(Function()
wait()
if enemy:FindFirstChild(“EnemyInfo”):FindFirstChild(“Health”) ~= nil then
local Health = enemy:FindFirstChild(“EnemyInfo”):FindFirstChild(“Health”)
else
return
end
end)

Do you mean game:GetService("RunService").Heartbeat:Connect()? Or is that a thing?

If I use @Vanniris 's fix, I get the same error on the line where it states:
if enemy:FindFirstChild(“EnemyInfo”):FindFirstChild(“Health”) ~= nil then

This also crashed my studio lol

Either enemy is nil, or it could not find “EnemyInfo”. That is the meaning of this error. Unfortunately you need to test to see where the error is. Try this:

local Health = nil
if enemy then
  print("Enemy found")
  if enemy:FindFirstChild("EnemyInfo") then
    print("EnemyInfo found")
    if enemy.EnemyInfo:FindFirstChild("Health") then
      print("Health found")
      Health =enemy.EnemyInfo.Health
    end
  end
end

The one that doesn’t print is the problem. I’m guessing enemy.

Edit: reviewing the original script, you do check for enemy. It’s possible your “for i, enemy” loop is not handling the pairs correctly. You can check that with a print(i) and print(enemy)

Also, a better idea for this would be to do a WaitForChild() instead.

local function ActiveScript(tower)
    while task.wait(0.1) do
        if TowerInfo:FindFirstChild("CooldownActive").Value == false then
            local towerRootPart = tower:WaitForChild("HumanoidRootPart")

            for _, enemy in pairs(Enemies:GetChildren()) do
                if enemy then
                    local Health = enemy:WaitForChild("EnemyInfo"):WaitForChild("Health", 0.1) -- timeout if not found, returning nil
                    if Health == nil then return end -- end the function if the enemy is dead
                    if enemy:FindFirstChild("Humanoid") and enemy:FindFirstChild("HumanoidRootPart") then
                        -- insert rest of code here
                    end
                end
            end
        end
    end
end

not sure if this was properly written. my programming skills are nuts.

image
I attempted this with the addition of printing the towers name that did it (I have TestTower, TestTower1 and TestTower2) after one enemy was killed, only TestTower1 went on, doing it’s job without issue.

Edit: Just noticed your edit, will check.

Ahh, this sounds suspiciously like the targets are dead, enemy is still “linked”, EnemyInfo is destroyed, and you need to select new targets.

Yeah I believe that probably is the case, what do you think I could do to make it select a new enemy to target?

You would need to build a function that triggers on death to transition to the new enemy. At the same time, you would need enough checks in this loop to skip a dead enemy.

I have a LOT of skips in my combat.

1 Like

Alright i’ll work on that, thanks for the help. :slight_smile:

1 Like