Requesting help with "attempt to perform arithmetic (sub) on string and number "

Hello there,

I am attempting to create a ban GUI. Most of it is working, however as I was testing a LocalScript, I received the following errors:

“Players.rzuffy.PlayerGui.BanGUI.BanFrame.LocalScript:100: attempt to perform arithmetic (sub) on string and number”

“Players.rzuffy.PlayerGui.BanGUI.BanFrame.LocalScript:103: attempt to concatenate string with boolean”

I am stuck and I do not know how to resolve this efficiently. The server and client scripts are attached below. Any help will be much appreciated.

Client:

local frame = script.Parent
local plr

local timeTable = {
["Permanent"] = "true",
["Months"] = "mo",
["Days"] = "d",
["Hours"] = "h",
["Minutes"] = "m"
}


function findPlayer(name)
for i,plr in pairs(game.Players:GetPlayers()) do
	name = name:lower()
	local testName = (plr.Name):lower():sub(1, #name)
	if(testName == name) then
		return plr
	end
end
end

function getThumbnail(plr)
return game.Players:GetUserThumbnailAsync(plr.UserId, Enum.ThumbnailType.HeadShot, Enum.ThumbnailSize.Size150x150)
end

function getUserId(name)
local success, id = pcall(function()
	return game:GetService("Players"):GetUserIdFromNameAsync(name)
end)
if(success) then
	return id
else
	warn(id)
end
end

function formatTime(timeLeft)
local timeString = ""
timeLeft = math.abs(timeLeft)
local function timeCheck(seconds, measure)
	if(math.floor(timeLeft / seconds) > 0) then
		timeString = timeString .. " " .. math.floor(timeLeft / seconds) .. " " .. measure .. " "
		timeLeft = timeLeft % seconds
	end
end
timeCheck(2629743, "Months")
timeCheck(86400, "Days")
timeCheck(3600, "Hours")
timeCheck(60, "Minutes")
timeString = timeString .. " " .. math.floor(timeLeft) .. " Seconds "
return timeString
    end

frame.PlayerSuggestion.Activated:Connect(function()
if(frame.PlayerSuggestion.Text ~= "") then
	frame.PlayerName.Text = frame.PlayerSuggestion.Text
end
end)

frame.PlayerName:GetPropertyChangedSignal("Text"):Connect(function()
if(frame.PlayerName.Text == frame.PlayerSuggestion.Text and frame.PlayerSuggestion.Text ~= "") then
	local success, content, ready = pcall(function()
		return getThumbnail(plr)
	end)
	if (success) then
		frame.PlayerImage.Image = content
	end
end
if(#frame.PlayerName.Text >= 2) then
	plr = findPlayer(frame.PlayerName.Text)
	if(plr) then
		frame.PlayerSuggestion.Text = plr.Name
	else
		plr = nil
	end
end
end)

frame.PlayerName.FocusLost:Connect(function()
if(not plr) then
	local userId = getUserId(frame.PlayerName.Text)
	if(userId) then
		plr = {
			["Name"] = frame.PlayerName.Text,
			["UserId"] = userId
		}
		local success, content, ready = pcall(function()
			return getThumbnail(plr)
		end)
		if(success) then
			frame.PlayerImage.Image = content
			local status = game:GetService("ReplicatedStorage"):WaitForChild("RequestInfo"):InvokeServer(plr)
			if(status) then
				if(status[1]) then
					frame.BanStatus.Text = "Banned"
					frame.BanStatusHeader.Text = "This player is banned!"
					frame.BanStatusHeader.TextColor3 = Color3.fromRGB(210,0,0)
					if(status[1] ~= true) then
						frame.BanStatus.Text = frame.BanStatus.Text .. " for " .. status[1]
					end
					if(status[2]) then
						frame.BanStatus.Text = frame.BanStatus.Text .. " by " .. status[2]
					end
					if(status[3]) then
						frame.BanStatus.Text = frame.BanStatus.text .. "\n Reason: " .. status[3]
					end
				end
			else
				frame.BanStatusHeader.Text = "This player isn't banned."
			    frame.BanStatusHeader.TextColor3 = Color3.fromRGB(255,255,255)
				frame.BanStatus.Text = ""
				
			end
		else
			warn(content)
		end
	end
end
end)



for i,v in pairs(frame.DMMenu:GetChildren()) do
if(v:IsA("GuiButton")) then
	v.Activated:Connect(function()
		frame.DM.Text = v.Text
		if(v.Text ~= "Permanent") then
			frame.Length.TextEditable = true
		else
			frame.Length.TextEditable = false
			frame.Length.Text = ""
		end
	end)
end
end

local deb = true
frame.DM.Activated:Connect(function()
if(deb) then
	deb = false
	if(frame.DMMenu.Visible == true) then
		wait(0.1)
		frame.DMMenu.Visible = false
	else
		frame.DMMenu.Visible = true
		wait(0.1)
	end
	deb = true
end
end)

frame.Ban.Activated:Connect(function()
if(plr) then
	if(frame.Length.TextEditable == false and frame.DM.Text == "Permanent") then
		game:GetService("ReplicatedStorage").BanRequest:FireServer(plr, frame.Reason.Text, "true")
	elseif(frame.Length.TextEditable == true and frame.DM.Text ~= "Permanent") then
		if(tonumber(frame.Length.Text)) then	
			game:GetService("ReplicatedStorage").BanRequest:FireServer(plr, frame.Reason.Text, (frame.Length.Text .. timeTable[frame.DM.Text]))
		end
	end
end
end)

frame.Unban.Activated:Connect(function()
if(plr) then
	game:GetService("ReplicatedStorage").BanRequest:FireServer(plr, false, false)
end
end)

Server:

local BanDataStore = game:GetService("DataStoreService"):GetDataStore("Bans2")
local mods = {39922681}

function formatTime(timeLeft)
	local timeString = ""
	timeLeft = math.abs(timeLeft)
	local function timeCheck(seconds, measure)
		if(math.floor(timeLeft / seconds) > 0) then
			timeString = timeString .. " " .. math.floor(timeLeft / seconds) .. " " .. measure .. " "
			timeLeft = timeLeft % seconds
		end
	end
	timeCheck(2629743, "Months")
	timeCheck(86400, "Days")
	timeCheck(3600, "Hours")
	timeCheck(60, "Minutes")
	timeString = timeString .. " " .. math.floor(timeLeft) .. " Seconds "
	return timeString
end

function setBanned(plr, reason, length, modName)
	local inGame = game:GetService("Players"):FindFirstChild(plr.Name)
	local key = "-Banned" .. plr.UserId
	local lenInNumber = tonumber(string.match(length, "%d+"))
	local seconds
	if(lenInNumber) then
		if(length:find("mo")) then
			seconds = lenInNumber * 2629743
		elseif(length:find("d")) then
			seconds = lenInNumber * 86400
		elseif(length:find("h")) then
			seconds = lenInNumber * 3600
		elseif(length:find("m")) then
			seconds = lenInNumber * 60
		end
		local curTime = os.time()
		local finalTime = curTime + seconds
		local timeLeft = (finalTime - curTime)
		
		local success, err = pcall(function()
			BanDataStore:SetAsync(key, {reason, finalTime, modName})
		end)
		if(not success) then
			print("An error occurred banning " .. plr.Name .. plr.UserId)
			warn(err)
		end
		if(inGame) then
			plr:Kick("\n You are banned from this game. \n Reason: " .. reason .. "\n Time left:" .. formatTime(timeLeft))
		end
	else
		length = length:lower()
		if(length == "perm" or length == "true" or length == "permanent" or length == "permban") then
			local success, err = pcall(function()
				BanDataStore:SetAsync(key, {reason, true, modName})
			end)
			if(not success) then
				print("An error occurred banning " .. plr.Name .. plr.UserId)
				warn(err)
			end
			if(inGame) then
				plr:Kick("\n You are permanently banned. \n Reason: " .. reason)
			end
		end
	end
end

game:GetService("Players").PlayerAdded:Connect(function(plr)
	local key = "-Banned" .. plr.UserId
	local success, val = pcall(function()
		return BanDataStore:GetAsync(key)
	end)
	
	if(success) then
		if(val) then
			if(val[2] == true) then
				plr:Kick("\n You are permanently banned. \n Reason: " .. val[1])
			elseif(tonumber(val[2])) then
				local length = os.time()
				if(length < val[2]) then
					plr:Kick("\n You are banned from this game. \n Reason: " .. val[1] .. "\n Time left:" .. formatTime(length - val[2]))
				else
					local success, err = pcall(function()
						BanDataStore:RemoveAsync("-Banned" .. plr.UserId)
					end)
					if(not success) then
						print("Unable to unban " .. plr.UserId .. " " .. plr.Name)
						warn(err)
					end
				end
			end
		end
	else
		print("An error occured while checking " .. plr.UserId .. " " .. plr.Name)
		warn(val)
	end
end)

game:GetService("ReplicatedStorage").BanRequest.OnServerEvent:Connect(function(mod, plr, reason, length)
	if(table.find(mods, mod.UserId)) then
		if(not table.find(mods, plr.UserId)) then
			if(reason ~= false and length ~= false) then
				setBanned(plr, reason, length, mod.Name)
			else
				local success, err = pcall(function()
					BanDataStore:RemoveAsync("-Banned" .. plr.UserId)
				end)
				if(not success) then
					print("Unable to unban " .. plr.UserId .. " " .. plr.Name)
					warn(err)
				end
			end
		end
	end
end)

game:GetService("ReplicatedStorage").RequestInfo.OnServerInvoke = function(mod, plr)
	if(table.find(mods, mod.UserId)) then
		local success, val = pcall(function()
			return BanDataStore:GetAsync("-Banned" .. plr.UserId)
		end)
		if(success) then
			if(val) then
				return {val[1], val[2], val[3]}
			else
				return nil
			end
		else
			print("Error getting status from " .. plr.UserId)
			warn(val)
		end
	end
end
3 Likes

Could you please show the code on the lines where it errors?

Of course. It errors at lines 100 and 103, and I’m really confused as to what is going on. I followed a tutorial to script this. This section of code below is the problem code, everything else works fine:

frame.PlayerName.FocusLost:Connect(function()
    	if(not plr) then
    		local userId = getUserId(frame.PlayerName.Text)
    		if(userId) then
    			plr = {
    				["Name"] = frame.PlayerName.Text,
    				["UserId"] = userId
    			}
    			local success, content, ready = pcall(function()
    				return getThumbnail(plr)
    			end)
    			if(success) then
    				frame.PlayerImage.Image = content
    				local status = game:GetService("ReplicatedStorage"):WaitForChild("RequestInfo"):InvokeServer(plr)
    				if(status) then
    					if(status[1]) then
    						frame.BanStatus.Text = "Banned"
    						frame.BanStatusHeader.Text = "This player is banned!"
    						frame.BanStatusHeader.TextColor3 = Color3.fromRGB(210,0,0)
    						if(status[1] ~= true) then
    							frame.BanStatus.Text = frame.BanStatus.Text .. " for " .. status[1]
    						end
    						if(status[2]) then
    							frame.BanStatus.Text = frame.BanStatus.Text .. " by " .. status[2]
    						end
    						if(status[3]) then
    							frame.BanStatus.Text = frame.BanStatus.text .. "\n Reason: " .. status[3]
    						end
    					end
    				else
    					frame.BanStatusHeader.Text = "This player isn't banned."
    				    frame.BanStatusHeader.TextColor3 = Color3.fromRGB(255,255,255)
    					frame.BanStatus.Text = ""
    					
    				end
    			else
    				warn(content)
    			end
    		end
    	end
    end)

These are the problem lines. The line with [1] is the 100 line error, and the one with [2] is the 103 line error.

if(status[1] ~= true) then
    frame.BanStatus.Text = frame.BanStatus.Text .. " for " .. status[1]
    end
    if(status[2]) then
    frame.BanStatus.Text = frame.BanStatus.Text .. " by " .. status[2]
    end
    if(status[3]) then
    frame.BanStatus.Text = frame.BanStatus.text .. "\n Reason: " .. status[3]
    end

Is status a String? Try:

frame.BanStatus.Text = tostring(frame.BanStatus.Text) .. " for " .. tostring(status[NumHere])

I forgot to mention, I was attempting this on the [1] line:

frame.BanStatus.Text = frame.BanStatus.Text .. " for " .. formatTime(status[1] - os.time())

This was designed to show the player how long they had left until their temporary ban expired.

If you refer to the formatTime function at the top of the Server code, you may have an idea what I’m trying to explain.

Ah, found the problem. status is a string, not a number, so you can’t subtract it from os.time()

Do this:

tonumber(status)

Alright, thank you. How do I add it to the line of code, though? I tried this but it produced the same error:

frame.BanStatus.Text = frame.BanStatus.Text .. " for " .. formatTime(tonumber(status[1]) - os.time())

That should work. Maybe try printing the status and typeof status?

print(status, typeof(status))

Actually, in Lua, numbers and strings is automatically converted when doing anything with each other (except when comparing); this will likely return nil.
In this instance, it tried to call the internal luaV_tonumber first, if it fails it’ll error so it’s (kind of) pointless to call tonumber.
https://www.lua.org/source/5.1/lvm.c.html#Arith

static void Arith (lua_State *L, StkId ra, const TValue *rb,
                   const TValue *rc, TMS op) {
  TValue tempb, tempc;
  const TValue *b, *c;
  if ((b = luaV_tonumber(rb, &tempb)) != NULL &&
      (c = luaV_tonumber(rc, &tempc)) != NULL) {
    lua_Number nb = nvalue(b), nc = nvalue(c);
    switch (op) {
      case TM_ADD: setnvalue(ra, luai_numadd(nb, nc)); break;
      case TM_SUB: setnvalue(ra, luai_numsub(nb, nc)); break;
      case TM_MUL: setnvalue(ra, luai_nummul(nb, nc)); break;
      case TM_DIV: setnvalue(ra, luai_numdiv(nb, nc)); break;
      case TM_MOD: setnvalue(ra, luai_nummod(nb, nc)); break;
      case TM_POW: setnvalue(ra, luai_numpow(nb, nc)); break;
      case TM_UNM: setnvalue(ra, luai_numunm(nb)); break;
      default: lua_assert(0); break;
    }
  }
  else if (!call_binTM(L, rb, rc, ra, op))
    luaG_aritherror(L, rb, rc);
}

What’s the value of status[1]? It could be malformed so tonumber fails.

We’re getting somewhere. Value [1] is to signify whether the player is temporarily banned or permanently banned (permanent = true, temporary = a time value). [2] is the moderator’s name and [3] is the reason for the ban.

Replacing that line with “print(status, typeof(status))” resulted in this:

image

I will look at the code again to try and resolve the issue via other means.

I have found my solution to the issue.

The order in return {val[1], val[2], val[3]} was incorrect, so I changed it to return {val[2], val[3], val[1]}. The incorrect order was giving the values to the wrong textboxes and caused a part of the issue.

The other part of the issue was solved by replacing the code as suggested by @THECOOLGENERATOR.

Thank you for taking the time to help me resolve my problem. I’m happy that I have it working correctly now! :smile: