Saving a DataStore Mixed Array Issue

So I’m currently creating a datastore that will hold player data even if they’re offline; it will only put data in for that player if the player has been warned (this is a warn system).

Here’s an example of the table that is being saved;

local WarnDB = {

  [41312758] = {

        ["Glitching"] = {"ModeratorName", "DateStuff"}

     }

}

However when attempting to save; all I get is this

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

If you read the error message, it’s pretty clear on what the problem is. You can’t have numbers as indexes (like 41312758) unless it’s an array. Try doing tostring on the player’s UserId so that it’s a string and not a number.

1 Like

the error you are getting is because data saved on roblox is automatically encoded in the JSON format, and you have broken one of the rules, all of the indexes must either be string or a number. As the previous replied said you use the tostring function, here is how you would do this:

local WarnDB = {

  [tostring(41312758)] = {

        ["Glitching"] = {"ModeratorName", "DateStuff"}

     }

}
--after when your loading back in the data you can use the tonumber function
local Table = --the table just saved
for index,value in pairs of (Table)do
     if tonumber(index) then -- checking if the index can be converted to a number
       index = tonumber(index)
       Table[index] = value
     end
end

1 Like

I tried using your loop and it pushed the same exact error;

for index,_ in pairs(cWarn_WarningDatabase) do
		     if tostring(index) then -- checking if the index can be converted to a number
		       index = tostring(index)
		     end
		end
		local success,issue = pcall(function()
			cWarn_Storage:UpdateAsync("WarningDatabase",function()
			
				return cWarn_WarningDatabase
			end)
		end)

The problem is caused by the Table being a Non-Array

The loop doesn’t actually change the Index into a string.

You need to do Table[tostring(Index)] = Value

But you would now have a Mixed & Non-Array Table so you need to do

Table[Index] = nil

Additionally you can only pass Dictionary and Array type Tables over the network.

Iirc by definition an Array doesn’t have any gaps between members but a Non-Array does.

1 Like

i edited the post try it now please and change your looop to this:

for index,value in pairs(cWarn_WarningDatabase) do
		     if tostring(index) then -- checking if the index can be converted to a number
		       index = tostring(index)
                       cWarn_WarningDatabase[index] = value
		     end
		end
		local success,issue = pcall(function()
			cWarn_Storage:UpdateAsync("WarningDatabase",function()
			
				return cWarn_WarningDatabase
			end)
		end)

One thing I noticed is the index “Glitching” might be used multiple times. Also, as said before, the user id should be a string otherwise the table becomes a non-array non-dictionary table.

I created a version of what I would consider the “ideal” format for this type of data. I use a timestamp as the index since the reason won’t be unique but the timestamp will. (I am also assuming the number is a Roblox user id)

local strings = {"Reason", "Moderator", "AccountAge"} -- A list of "constant" strings used by the datastore which can be used for readability when debugging or outputting data.
-- The index of each string is the "id." The id is used as an index for some data.
-- The reason I do this is it allows the key name to be changed later but also conserves space by assigning each index a smaller string value.
local WarnDB = {
	[tostring(subject.UserId)] = {
		[tostring(os.time() + tick()%1)] = {
			["1"] = "Glitching",
			["2"] = moderator.UserId,
			["3"] = subject.AccountAge -- In days, and not their irl age. Basically the time since their account was created. Allows you to see how long ago their account was made and also get the date that it was made.
		}
	}
}

This will give you an accurate time with less accurate < 1 second times as your warning indexes. Since it is literally impossible to define the table twice with the same tick() value you’ll always have a unique index as well.

I also replaced the moderator name with a userid since usernames can change. You can get a username from a userid using Players:GetNameFromUserIdAsync(userId) or using the web api.