Invalid argument #1 to 'find' (table expected, got nil)

I was testing a module I made and for some reason when I added prints to check what was going on with an error, and it turns out that the table self.Listeners in the module seems to be getting nil later on the code.

Print Outputs:

  19:58:51.329   ▼  {
                    ["List"] =  ▶ {...},
                    ["Listeners"] = {}
                 }  -  Server - Broom:15
  19:58:51.329  {}  -  Server - Broom:21
  19:58:52.621  nil  -  Server - Broom:33

Error Stack Calls:

  19:58:52.621  ReplicatedStorage.Broom:35: invalid argument #1 to 'find' (table expected, got nil)  -  Server - Broom:35
  19:58:52.622  Stack Begin  -  Studio
  19:58:52.622  Script 'ReplicatedStorage.Broom', Line 35 - function RemoveListener  -  Studio - Broom:35
  19:58:52.622  Script 'ReplicatedStorage.Broom', Line 98 - function ClearListenersFromList  -  Studio - Broom:98
  19:58:52.622  Script 'ServerScriptService.Script', Line 24  -  Studio - Script:24
  19:58:52.622  Stack End

Code:

local broom = {}
broom.__index = broom

function broom.new() -- Inits self
	local self = setmetatable({}, broom)

	self.List = {}
	self.Listeners = {}

	return self
end

function broom:AddListener(listener : any, list : any?) -- Adds a listener to a regular table, (listener ::  must be a RBXScriptConnection or a thread), (list : optional arguement, adds a listener to a list you've created)\
	assert(listener, "No listener provided")
	print(self)
	assert(typeof(listener) == "RBXScriptConnection" or typeof(listener) == "thread", "Listener is not a connection or thread")
	list = list and tostring(list)

	if typeof(listener) == "RBXScriptConnection" or typeof(listener) == "thread" then
		if list and self.List[list] then
			print(self.Listeners)
			table.insert(self.List[list], listener)
		else
			table.insert(self.Listeners, listener)
		end
	end
end

function broom:RemoveListener(listener : any, list : any?) -- Removes a listener from a regular table, (listener ::  must be a RBXScriptConnection or a thread), (list : optional arguement, removes a listener from a list you've created)
	list = list and tostring(list)
	assert(listener, "No listener provided")
	assert(typeof(listener) == "RBXScriptConnection" or typeof(listener) == "thread", "Listener is not a connection or thread")
	print(self.Listeners)
	print(listener)
	assert(list and table.find(self.Listeners, listener), "Listener not found in regular table")
	if list and not table.find(self.List[list], listener) then
		return error("Listener not found in List: "..list, 2)
	end

	if typeof(listener) == "RBXScriptConnection"  then
		if list and self.List[list] then
			local index = table.find(self.List[list], listener)
			if index then
				listener:Disconnect()
				table.remove(self.List[list], index)
			end
		else
			local index = table.find(self.Listeners, listener)
			if index then
				listener:Disconnect()
				table.remove(self.Listeners, index)
			end
		end
	elseif typeof(listener) == "thread" then
		if list and self.List[list] then
			local index = table.find(self.List[list], listener)
			if index then
				task.cancel(listener)
				table.remove(self.List[list], index)
			end
		else
			local index = table.find(self.Listeners, listener)
			if index then
				task.cancel(listener)
				table.remove(self.Listeners, index)
			end
		end
	end
end

function broom:ClearListeners() -- Clears all listeners from the regular table
	assert(#self.Listeners > 0, "No listeners to clear")
	for _, listener in self.Listeners do
		if typeof(listener) == "RBXScriptConnection" then
			listener:Disconnect()
		else
			task.cancel(listener)
		end
	end
	self.Listeners = {}
end

function broom:CreateList(listName : string) : any -- Creates a list to store specific listeners
	assert(listName, "No list name provided")
	assert(not self.List[listName], "List already exists in table")

	if not self.List[listName] then
		self.List[listName] = {}
	end
end

function broom:ClearListenersFromList(listName : string) -- Clears all listeners from a specific list
	assert(listName, "No list name provided")
	assert(self.List[listName], "List does not exist in table")
	listName = listName and tostring(listName)
	if self.List[listName] then
		for _, listener : any in self.List[listName] do
			broom:RemoveListener(listener, listName)
		end
		self.List[listName] = nil
	end
end

return broom

Any help is appreciated : )

The issue lies most likely in how you’re calling RemoveListener. Make sure you’re invoking this method on an instance of the broom class, and not the broom class itself. For example:

local object = Class.new()
object:Method()

Where you might be doing:

local object = Class.new()
Class:Method()
1 Like

I think you wanted this:

		for _, listener : any in self.List[listName] do
			self:RemoveListener(listener, listName)
		end

As Ziffix notes, Lua will let you call the template method. In your case, you’re doing it from within the class itself though, but the result is the same, self.Listeners is not the value you expect, because it’s pointing to broom.Listeners, which doesn’t exist.

1 Like

self.Listeners stopped being nil, however this assert runs for no reason?

assert(list and table.find(self.Listeners, listener), "Listener not found in regular table")

Break up your asserts, so that when it actually asserts, you know which part is the cause. Make
assert(list and table.find(self.Listeners, listener) into multiple asserts, something like:

assert(list)
assert(listener)
assert(table.find(self.Listeners, listener))

Or, alternatively, just print all the values, including list, so you know why the assert is failing. With a compound expression inside your assert, you can’t narrow it down as easily.

Lastly, if this assert fails every time, you could just put a breakpoint on it and look at all the values in the Watcher window.

I replaced the assert with

if not list and not table.find(self.Listeners, listener) then
	error("Listener not found in regular table", 2)
end

Because the assert had no reason to print the error, it was only supposed to print the error if both values were nil. I guess asserts kinda suck for some parts.

It’s not the assert that sucks, if you wanted an error only if both values were nil, you had the logic wrong. Your new logic of:

if not list and not table.find(self.Listeners, listener) then

is equivalent to:

assert(list or table.find(self.Listeners, listener), "Listener not found in regular table")

But you had:

assert(list and table.find(self.Listeners, listener), "Listener not found in regular table")

Asserts throw when their expression is false or nil, so assert(A and B) means assert when not(A and B) == true, which is equivalvent to ((not A) or (not B)) == true but not equivalent to ((not A) and (not B)) == true, which is what you wanted.

Oh, I thought asserts can support ands it was my first time using asserts. But thank you for guiding me through this.