"DataStores cannot store Instance" when not attempting to store an Instance

Hi!

The error I’m getting is very clearly saying I cant store an instance in the DataStore, but I’m not trying to store an instance, only the properties.
Module function:

function module.SaveData(player, plot, ID)
	--// player : the player
	--// plot : the 'BuildPlot'
	local PlayerData = {}
	local BaseCFrame = game.Workspace.Plots[plot.Name].BuildPlot.CFrame
	for _, item in pairs(plot:GetChildren()) do
		if item:IsA("Model") then
			table.insert(PlayerData, {
				item.Name;
				table.pack(item.PrimaryPart.CFrame:GetComponents())
			})
		elseif item:IsA("MeshPart") or item:IsA("Part") or item:IsA("UnionOperation") then
			table.insert(PlayerData, {
				item.Name;
				table.pack(item.CFrame)
			})
		end
	end
	game.ReplicatedStorage.Datastore.SaveHouse:FireServer(ID, PlayerData)
end

Server:


local DS = game:GetService("DataStoreService"):GetDataStore("FYT2-Build-Plots")

game.ReplicatedStorage.Datastore.SaveHouse.OnServerEvent:Connect(function(ID, Data)
	local succ, err = pcall(function()
		DS:SetAsync(ID, Data)
	end)
	if succ then print("Saved!") return true elseif not succ then warn(err) return end
end)
game.ReplicatedStorage.Datastore.GetData.OnServerEvent:Connect(function(ID)
	local succ, err = pcall(function()
		DS:GetAsync(ID)
	end)
	if succ then print("Got data!") return DS:GetAsync(ID) elseif not succ then warn(err) return end
end)

Thanks for any help!

I don’t believe CFrames are a string value. I think you have to do something like

local string = tostring(item.CFrame)

I think that’s what the issue is here, although I’m most probably wrong.

1 Like
			table.insert(PlayerData, {
				item.Name;
				table.pack(tostring(item.CFrame))
			})

I now have this, I have the same error.

I don’t think you can directly convert a CFrame to a string. Instead, you could try storing a CFrame in another table with each of its components converted to a string. (if you don’t really need the orientation, just serialize and use the object’s position instead.)

local function serializeCFrame(cf)
return {cf:GetComponents()} --//Add this to the user's data
end

Do I do table.pack(tostring(serializeCFrame(item.CFrame)))?

Nope. What I have works as a standalone solution, just add what the function returns to the player’s data.

You can also deserialize the CFrame (or read it from player data) using something similar:

local function deserializeCF(CF)
return CFrame.new(unpack(CF))
end
1 Like

image
Nope. Same error, trying that new code.

That is also resulting in the error.

I used the tostring() value and it saved and loaded for me,

local DS = game:GetService("DataStoreService")
local store = DS:GetDataStore("Data")
local string = tostring(workspace.Dummy.LeftFoot.CFrame)

store:SetAsync("1Minecraft0954",string)

When I removed the tostring() from the equation, Roblox produced error 104: Cannot store CoordinateFrame in data store.

I also spotted the problem,

I think you’re using the actual player instance in the ‘key’ parameter when you call :SetAsync(key, value)

It expects the player’s UserId, so pretty quick fix:

--//Note, ID is actually the player calling the remote, so maybe rename it to Player
DS:SetAsync(ID.UserId, Data)

All RemoteEvents implicitly (hiddenly) passes the player who’s calling the remote as the first argument, this is always the case.

The key is a Debug ID. It inCludes the user’s ID. It’s formatted FYTP-UserID-RandomID

The Debug ID is making it easier to display plots in a list, and moderation + loading plots

They’re RemoteEvents though. RemoteEvents always pass through the player calling the remote as the first parameter, so when you index ID, you’re actually indexing the player, which is of type Instance.

Just add .UserId at the end in both OnServerEvent functions and see if it yields you success.

1 Like

Ah yes, I just realised what the issue is I think. As Sharpie mentioned, client - server remote events send the player who sent the information. Therefore, the server is reading the player who sent it, then their ID. So I think your server is trying to store the player’s ID instead of the data you want.
I think you can safely remove ID from the client’s remoteevent.

1 Like

This might just be my issue at this point, but I did save the house, but it’s saying the data is nil.
image

Again, could be something totally unrelated.

	if data == nil then 
		print("No house data for plot!")
				data = game.ReplicatedStorage.Datastore.GetData:FireServer(StarterHouseID)
		print("Sent request to load StarterHouse")
		task.wait(2)
	end
	for _, item in pairs(data) do
--//Other code made useless bc for some reason the data is nil.

Hi Pixlz, what script is giving the error? Are you able to save data or is it saving as nil?

It’s saving as nil.

You can’t coerce a CFrame value into a primitive string value (such that it can be stored in a DataStore) by passing it as an argument to the “tostring()” global function, instead you’d need to compartmentalise the CFrame value into its components and then store those in a string/table value. For example.

local CFrameValue = workspace.Part.CFrame
local SerializedCFrame = table.pack(CFrameValue:GetComponents())
print(#SerializedCFrame) --this table value can be stored in a DataStore

This gets all of the components which describe the CFrame value and then packs them into a table with the “table.pack” table library function and then prints the number of components.

This serialized data can easily be reverted back into its deserialized state when loading by doing the following.

local DeserializedCFrame = CFrame.new(table.unpack(SerializedCFrame))

Here, we unpack the components with the “table.unpack” table library function and subsequently pass each as an argument to the CFrame class constructor function in order to construct a CFrame value out of its components.

1 Like

The issue with this is when “FireServer()” is being called on the RemoteEvent instance named “SaveHouse” two optional values are being passed as arguments to the instance method, which means that on the listening side any callback function connected to the corresponding “OnServerEvent” of the same RemoteEvent instance will need to be defined with three explicitly declared parameters, one to handle automatically passed player object as you correctly stated, and two to handle the additional two values passed to “FireServer()”, as your suggestion ignored this second optional value passed to “FireServer()” it ends up being discarded (resulting in the callback not working as intended).

game.ReplicatedStorage.Datastore.SaveHouse:FireServer(ID, PlayerData) --two optional values passed to fireserver()
game.ReplicatedStorage.Datastore.SaveHouse.OnServerEvent:Connect(function(Player, ID, Data) --connected callback function must declare three parameters, one to handle the received player object and another two to handle the two optional values
	
game.ReplicatedStorage.Datastore.GetData:FireServer(StarterHouseID) --optional value passed to fireserver()
game.ReplicatedStorage.Datastore.GetData.OnServerEvent:Connect(function(Player, ID) --connected callback function must declare two parameters, one to handle the received player object and another to handle the optional value

Here’s how both “FireServer()” calls and corresponding “.OnServerEvent” events should be written. With this, the definitions of those callback functions can be changed to incorporate the player object which fired the server, if necessary.

Yeah I know, I was trying to help him understand that the ID parameter is actually the player.

1 Like