Datastores: Passing invalid table values should produce more descriptive errors

As a Roblox developer, it is difficult to deal with errors from Datastore related to storing invalid values, such as tables with invalid indices/keys, because the errors don’t give any additional information on why the value is invalid for storage in Datastores.


Examples of how inconclusive the current error messages are:

data:SetAsync("TestKey", {1, 2, 3, game})
> 104: Cannot store Array in DataStore

data:SetAsync("TestKey", {1,{2,3,function() end},4,5})
> 104: Cannot store Array in DataStore

data:SetAsync("TestKey", {a = 1, b = 2, c = game})
> 104: Cannot store Dictionary in DataStore

data:SetAsync("TestKey", {a = 1, b = {c = function() end, d = 3}, e = 4})
> 104: Cannot store Dictionary in DataStore

data:SetAsync("TestKey", {[game] = 1, b =2, c = 3})
> Cannot convert mixed or non-array tables: keys must be strings

data:SetAsync("TestKey", {a = 1, b = {c = 2, [true] = 3}, e = 4})
> Cannot convert mixed or non-array tables: keys must be strings

I would suggest that something along the lines of the following is thrown instead, respective to the six calls above:

Cannot store Array in DataStore: value at input[4] is of invalid type Instance
Cannot store Array in DataStore: value at input[2][3] is of invalid type function
Cannot store Dictionary in DataStore: value at input.c is of invalid type Instance
Cannot store Dictionary in DataStore: value at input.b.c is of invalid type function
Cannot store Dictionary in DataStore: input contains an index of invalid type Instance
Cannot store Dictionary in DataStore: input.b contains an index of invalid type boolean

Note that the exact path to where the issue occurs is given.

I personally use the following code (original version by @Corecii) to generate descriptive warnings/errors when I try to store invalid data. Including here for reference. It will describe the path to the invalid keys/values in the table and exactly the issue that occurred in the respective (sub-)table. If something like this could be internalized into the DataStoreService API, I think it would help out many developers in better understanding what data they can and can’t store, and to track down issues in their code that generated such invalid tables much quicker.

Scan function code
-- checks through the entire passed value/table and gives back a path to invalid key/value
local function scanValidity(tbl, passed, path)
	if type(tbl) ~= "table" then
		return scanValidity({input = tbl}, {}, {})
	end
	passed, path = passed or {}, path or {"root"}
	passed[tbl] = true
	local tblType
	do
		local key = next(tbl)
		if type(key) == "number" then
			tblType = "Array"
		else
			tblType = "Dictionary"
		end
	end
	local last = 0
	for key, value in next, tbl do
		path[#path + 1] = tostring(key)
		if type(key) == "number" then
			if tblType == "Dictionary" then
				return false, path, "cannot store mixed tables"
			elseif key%1 ~= 0 then
				return false, path, "cannot store tables with non-integer indices"
			elseif key == math.huge or key == -math.huge then
				return false, path, "cannot store tables with (-)infinity indices"
			end
		elseif type(key) ~= "string" then
			return false, path, "dictionaries cannot have keys of type " .. typeof(key)
		elseif tblType == "Array" then
			return false, path, "cannot store mixed tables"
		end
		if tblType == "Array" then
			if last ~= key - 1 then
				return false, path, "array has non-sequential indices"
			end
			last = key
		end
		if type(value) == "userdata" or type(value) == "function" or type(value) == "thread" then
			return false, path, "cannot store value '" .. tostring(value) .. "' of type " .. typeof(value)
		end
		if type(value) == "table" then
			if passed[value] then
				return false, path, "cannot store cyclic tables"
			end
			local isValid, keyPath, reason = scanValidity(value, passed, path)
			if not isValid then
				return isValid, keyPath, reason
			end
		end
		path[#path] = nil
	end
	passed[tbl] = nil
	return true
end

This example code also checks for issues reported in https://devforum.roblox.com/t/--/176247, regarding mixed tables, arrays not starting at 1 / arrays with holes, and cyclic tables.


If this issue is addressed, it would improve my development experience because I would have to spend less time debugging which parts of my table were invalid and why those parts were invalid. I would just be able to read the error and know where to look for issues in my code.

10 Likes

Cannot convert non-array or mixed tables: keys must be strings
^^^^
regarding this error

I think this error is still very uninformative, it doesn’t tell you what the datastore that failed is, the line of code, the script, the value or the key. I get this error very often and it always takes me much longer to solve than all of the other errors I receive, mainly due to the fact that it’s so difficult to understand or locate the source of.

It also doesn’t seem to make much grammatical or logical sense, before finding this post I was very confused as to what the error actually meant, as it seems to contradict itself:

Cannot convert non-array or mixed tables: keys must be strings

The ‘keys must be strings’ part seems like its referencing the entire sentence coming before the colon, and since arrays cannot have strings as keys, it seems like a direct contradiction of itself. On top of that, it doesn’t say anything about the fact that the datastore will accept arrays, or dictionaries, but not a mixture. It actually says cannot convert non-array tables , making it sound like your data must be an array, when in fact it can be a string indexed dictionary.

1 Like