Pathfinding AI getting stuck for no apparent reason

Let me first explain how the zombie works:

The Green Line is me walking around the block and then up the stairs.
The Red Line is the Zombie walking behind me but stopping as soon as it reaches that point.

The AI is advanced so it knows how to unstuck itself and use stairs but for some reason, it just gets stuck there.

Any idea how to fix it? (No zombies/players should not be able to jump in this game)

Server-Side script:

local myHuman = script.Parent:WaitForChild("Humanoid")
local myRoot = script.Parent:WaitForChild("HumanoidRootPart")
local head = script.Parent:WaitForChild("Head")
local lowerTorso = script.Parent:WaitForChild("LowerTorso")
--local grab = script.Parent:WaitForChild("Grab")
--local grabAnim = myHuman:LoadAnimation(grab)
--grabAnim.Priority = Enum.AnimationPriority.Action

--local grabSound = head:WaitForChild("Attack")
--local screamSound = head:WaitForChild("Scream")

local barrier = false

local clone = script.Parent:Clone()

for _, object in pairs(script.Parent:GetChildren()) do 
	if object:IsA("BasePart") then 
		object:SetNetworkOwner()
	end
end

function walkRandomly()
	local xRand = math.random(-50,50)
	local zRand = math.random(-50,50)
	local goal = myRoot.Position + Vector3.new(xRand,0,zRand)

	local path = game:GetService("PathfindingService"):CreatePath()
	path:ComputeAsync(myRoot.Position, goal)
	local waypoints = path:GetWaypoints()

	if path.Status == Enum.PathStatus.Success then
		for _, waypoint in ipairs(waypoints) do
			myHuman:MoveTo(waypoint.Position)
			local timeOut = myHuman.MoveToFinished:wait(2)
			if not barrier then
				if not timeOut then
					print("Got stuck")
					walkRandomly()
				end
			end
			
		end
	else
		print("Path failed")
		walkRandomly()
	end
end

function findPath(target)
	local path = game:GetService("PathfindingService"):CreatePath()
	path:ComputeAsync(myRoot.Position,target.Position)
	local waypoints = path:GetWaypoints()

	if path.Status == Enum.PathStatus.Success then
		for _, waypoint in ipairs(waypoints) do
			myHuman:MoveTo(waypoint.Position)
			local timeOut = myHuman.MoveToFinished:wait(2)
			if not timeOut then
				
				print("Path too long!")
				findPath(target)
				break
			end
			if checkSight(target) then
				repeat
					print("Moving directly to the target")
					myHuman:MoveTo(target.Position)
					attack(target)
					wait(0.1)
					if target == nil then
						break
					elseif target.Parent == nil then
						break
					end
				until checkSight(target) == false or myHuman.Health < 1 or target.Parent.Humanoid.Health < 1
				break
			end
			if (myRoot.Position - waypoints[1].Position).magnitude > 20 then
				print("Target has moved, generating new path")
				findPath(target)
				break
			end
		end
	end
end

function checkSight(target)
	local ray = Ray.new(myRoot.Position, (target.Position - myRoot.Position).Unit * 40)
	local hit,position = workspace:FindPartOnRayWithIgnoreList(ray, {script.Parent})
	if hit then
		if hit:IsDescendantOf(target.Parent) then
			print("I can see the target")
			return true
		end
	end
	return false
end

function findTarget()
	local dist = 1000
	local target = nil
	local potentialTargets = {}
	local seeTargets = {}
	for i,v in ipairs(workspace:GetChildren()) do
		local human = v:FindFirstChild("Humanoid")
		local torso = v:FindFirstChild("Torso") or v:FindFirstChild("HumanoidRootPart")
		if human and torso and v.Name ~= script.Parent.Name then
			if (myRoot.Position - torso.Position).magnitude < dist and human.Health > 0 then
				table.insert(potentialTargets,torso)
			end
		end
	end
	if #potentialTargets > 0 then
		for i,v in ipairs(potentialTargets) do
			if checkSight(v) then
				table.insert(seeTargets, v)
			elseif #seeTargets == 0 and (myRoot.Position - v.Position).magnitude < dist then
				target = v
				dist = (myRoot.Position - v.Position).magnitude
			end
		end
	end
	if #seeTargets > 0 then
		dist = 200
		for i,v in ipairs(seeTargets) do
			if (myRoot.Position - v.Position).magnitude < dist then
				target = v
				dist = (myRoot.Position - v.Position).magnitude
			end
		end
	end
	return target
end
local attacking = false
function attack(target)
	if (myRoot.Position - target.Position).magnitude < 2 then
		if not attacking then
			attacking = true
			if target.Parent ~= nil then
				myHuman.WalkSpeed = 8
				wait(0.1)
				if (myRoot.Position - target.Position).magnitude < 2 then
					target.Parent.Humanoid:TakeDamage(25)
				end
			end
			wait(0.2)
			myHuman.WalkSpeed =  14
			wait(0.2)
			attacking = false
		end
	end
end

function died()
	wait(5)
	clone.Parent = workspace
	game:GetService("Debris"):AddItem(script.Parent,0.1)
end

myHuman.Died:Connect(died)

lowerTorso.Touched:Connect(function(obj)
	if not obj.Parent:FindFirstChild("Humanoid") then
		
	end
end)

function main()
	local target = findTarget()
	if target then
		findPath(target)
	else
		walkRandomly()
	end
end

while wait(0.1) do
	if myHuman.Health < 1 then
		break
	end
	main()
end

Specifications about zombie AI

  • Zombie speed is 12 when it’s running and 8 when it’s attacking.
  • Zombie also walks randomly if it can’t find a player.
  • Max distance is 1k studs because it should find players around the map and walk to them.

Solution Found

So the problem wasn’t the AI or anything, it was just because the cap between the 2 walls surrounding the stairs was too small and counts as a wall if the zombie isn’t in front of it.

you could also disable jumps by settings the humanoid jumppower to 0