InvokeServer return truncated dictionary when keys are numeric

Reproduction Steps

  1. Put this on a Server Script:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RemoteFunction = ReplicatedStorage:WaitForChild("RemoteFunction")

RemoteFunction.OnServerInvoke = function(Player)
	local ServerDic1 = {[1] = "a", [3] = "b"}
	local ServerDic2 = {[1] = "a", [2] = "b"}
	local ServerDic3 = {["1"] = "a", ["3"] = "b"}
	print("ServerDic1", ServerDic1)
	print("ServerDic2", ServerDic2)
	print("ServerDic3", ServerDic3)
	return  ServerDic1, ServerDic2, ServerDic3
end
  1. … and this on a LocalScript:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RemoteFunction = ReplicatedStorage:WaitForChild("RemoteFunction")

local LocalDic1, LocalDic2, LocalDic3 = RemoteFunction:InvokeServer(game.Players.LocalPlayer)
print("LocalDic1", LocalDic1)
print("LocalDic2", LocalDic2)
print("LocalDic3", LocalDic3)
  1. Run and compare the output

Expected Behavior
ServerDic1 / LocalDic1 should be the same.

Actual Behavior
LocalDic1 gets only the first element. Item [3] is not there, after getting the dictionary from Server:

Workaround
Convert all keys to numeric before returning from Server

Issue Area: Engine
Issue Type: Other
Impact: Moderate
Frequency: Constantly

3 Likes

This is a common limitation with most Roblox API where the contents are serialized or sent over network (remotes, datastores, etc): they only accept dictionaries and proper arrays, otherwise they cut off either the dictionary or the array parts silently.

{ [1] = “a”, [3] = “b” } is not a proper array since while it does start at 1, it has a hole at [2].

{ [“1”] = “a”, [“3”] = “b” } will work fine since this is a dictionary. It’s unrelated to the keys being stringified numbers, any table with just string keys is a dictionary.

If you were to try something like { [1] = “a”, [2] = “b”, test = “c” } you would also see truncation, because this is a mixed table (has an array and a dictionary part).

It’s inconvenient for sure and buggy that it cuts it off silently, but I typically find that if you end up with mixed tables or arrays with holes in your architecture, it’s indicative of a code smell somewhere in your implementation. Using proper arrays and dictionaries gives you less of a headache since you don’t need to special-case if you ever need to serialize things (via Roblox API or yourself when JSON encoding/decoding). Some table APIs also don’t really handle the array with holes situation well IIRC.

4 Likes

In places where variants are accepted in the Roblox engine, for tables, only arrays without gaps and dictionaries of string → Variant are supported.

2 Likes

I spent a few hours debugging my game until I found out that this was the cause of the problem. Now that I know this is a default engine behavior, I think I won’t waste that much time next time.
However, this behavior should be documented in RemoteFunction.OnServerInvoke and RemoteFunction | Roblox Creator Documentation.
And if Roblox could be a little more charitable to developers, it could create an alert when it detects a similar situation.
I’m sure this would help many developers not waste so much time for the same reason.

3 Likes

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.