String is nil when passed through remote event

Hello!

I have two scripts (not the entirety of both scripts)

Local:

local tools = player.Backpack:GetChildren()

		local toolTable = {}

		
		for _, toolSing in ipairs(tools) do
			table.insert(toolTable, toolSing.Name)
			print(toolSing.Name .. " added to table")
		end

		
		print(table.concat(toolTable, ", "))

		
		for _, toolSin in ipairs(toolTable) do
			print(toolSin .. " is in table")
			RemoveSoldItem:FireServer(plr, toolSin) 
		end

ServerScript:

RemoveSoldItem.OnServerEvent:Connect(function(plr, toolItemName)
	print(toolItemName)

	if not tools[plr] then
		warn("No tools found for player: " .. plr.Name)
		return
	end


	local TargetPosition = table.find(tools[plr], toolItemName)

	print("Current tools for player:", table.concat(tools[plr], ", "))

	if TargetPosition then
		print("Removing tool at position:", TargetPosition)
		table.remove(tools[plr], TargetPosition)
	else
		warn("Tool not found in player's inventory")
	end
end)

In the ServerScript toolItemName is always nil :frowning:

Any solutions?

1 Like

Remove the plr argument from FireServer. It’s always passed along automatically.

I still get this warning do you know why (It’s in the ServerScript)
18:38:13.460 Tool not found in player’s inventory - Server - ToolSaving:97

Can you please print out the contents of tools[plr], and the value of toolItemName?

image_2025-02-19_184055871

the hammer is the toolItemName’s value.
And the {} is what the tools[plr] prints out.

Based on that, the following line is wrong

it should be

if #tools[plr] == 0 then

That doesn’t print out “Tool not found in player’s inventory”
or “Removing tool at position:”, TargetPosition"

It also doesn’t run anything or print anything after:

if #tools[plr] == 0 then
		warn("No tools found for player: " .. plr.Name)
		return
	end

Can you heart this post to indicate that you’ve read this?

This is quite confusing for this small of an issue.

Can you please print out

#tools[plr]

?

it prints out nil.


Here is how contents get inserted to the table:

Players.PlayerRemoving:Connect(function(client)

	local key = "client_" .. client.UserId
	tools[client] = { }

	local Character = client.Character
	local Backpack = client.Backpack
	if Character then
		for X, item in Character:GetChildren() do
			if not item:IsA("Tool") then continue end

			table.insert(tools[client], item.Name)
		end
	end

	for _, item in ipairs(client.Backpack:GetChildren()) do
		if item:IsA("Tool") then
			print(item.Name)
			table.insert(tools[client], item.Name)
		else
			warn(item.Name .. " is not a Tool")
		end
	end

	print(tools[client])
	player_data:UpdateAsync(key, function(prev)
		
		return tools[client]
		
	end)
end)

At this point, just show us the full script

local Players = game:GetService("Players")
local DataStoreService = game:GetService("DataStoreService")
local ServerStorage = game:GetService("ServerStorage")
local RepliStorage = game:GetService("ReplicatedStorage")

local player_data = DataStoreService:GetDataStore("TestData1")
local RemoveSoldItem = RepliStorage.Remotes.RemoveSoldItem

local toolsInstance = ServerStorage.Tools

local tools = {}

Players.PlayerAdded:Connect(function(client)
	client.CharacterAdded:Connect(function(plrChar)
		print(tools[client])
		tools[client] = {}
		
		local key = "client_" .. client.UserId
		local inventory = player_data:GetAsync(key) 
		print(inventory)
		
		
		
		for i, name in inventory do
			print(name)
			local tool = game:GetService("ReplicatedStorage").ItemAssets:FindFirstChild(name)
			local Backpack = client.Backpack
			print(tool)

			if tool then
				print(tool.Name)
				local Clonedtool = tool:Clone()
				Clonedtool.Name = tool.Name
				Clonedtool.Parent = Backpack
				print("tool cloned")
			else
				warn("Item could not be cloned")
			end
		end
		
		RemoveSoldItem.OnServerEvent:Connect(function(plr, toolItemName)
			

			if #tools[plr] == 0 then
				warn("No tools found for player: " .. plr.Name)
				
			end


			local TargetPosition = table.find(tools[plr], toolItemName)
			print(tools[plr])
			print(toolItemName)

			print("Current tools for player:", table.concat(tools[plr], ", "))

			if TargetPosition then
				print("Removing tool at position:", TargetPosition)
				table.remove(tools[plr], TargetPosition)
			else
				warn("Tool not found in player's inventory")
			end
		end)

		
	end)
	

end)

Players.PlayerRemoving:Connect(function(client)

	local key = "client_" .. client.UserId
	tools[client] = { }

	local Character = client.Character
	local Backpack = client.Backpack
	if Character then
		for X, item in Character:GetChildren() do
			if not item:IsA("Tool") then continue end

			table.insert(tools[client], item.Name)
		end
	end

	for _, item in ipairs(client.Backpack:GetChildren()) do
		if item:IsA("Tool") then
			print(item.Name)
			table.insert(tools[client], item.Name)
		else
			warn(item.Name .. " is not a Tool")
		end
	end

	print(tools[client])
	player_data:UpdateAsync(key, function(prev)
		
		return tools[client]
		
	end)
end)

Good lord. Remove that embedded RemoteEvent.OnServerEvent handler immediately

Why?


I see nothing that inserts into the player’s cached inventory during their lifetime in the server. You’re only doing this when the player disconnects, which has no relevance to your RemoveSoldItem event

What do I substitute with toolItemName?

local TargetPosition = table.find(tools[client], toolItemName)

Each time a player enters your game and (re)spawns, you will clone the event listener to RemoveSoldItem.OnServerEvent. This can lead to serious adverse collateral damage as you begin to duplicate your response to the event. It is also a greedy memory leak, which generally has the potential to seriously impact performance as the game continues on. There is zero necessity for your event listener to be characterized this way—it doesn’t even use its immediate upvalues

Move the event listener into the main scope away from Players.PlayerAdded

Like this? Also look at my previous post.

Players.PlayerAdded:Connect(function(client)
	client.CharacterAdded:Connect(function(plrChar)
		print(tools[client])
		tools[client] = {}
		
		local key = "client_" .. client.UserId
		local inventory = player_data:GetAsync(key) 
		print(inventory)
		
		
		
		for i, name in inventory do
			print(name)
			local tool = game:GetService("ReplicatedStorage").ItemAssets:FindFirstChild(name)
			local Backpack = client.Backpack
			print(tool)

			if tool then
				print(tool.Name)
				local Clonedtool = tool:Clone()
				Clonedtool.Name = tool.Name
				Clonedtool.Parent = Backpack
				print("tool cloned")
			else
				warn("Item could not be cloned")
			end
		end
		
		-- Here
			

			if #tools[client] == 0 then
				warn("No tools found for player: " .. client.Name)
				
			end


			local TargetPosition = table.find(tools[client], toolItemName)
			print(tools[client])
			print(toolItemName)

			print("Current tools for player:", table.concat(tools[plr], ", "))

			if TargetPosition then
				print("Removing tool at position:", TargetPosition)
				table.remove(tools[client], TargetPosition)
			else
				warn("Tool not found in player's inventory")
			end
		end)

		
	
	

end)

Use pairs(inventory) instead of inventory