RemoteFunctions break completely after passing specific mixed tables

Many of us have encountered the error Cannot convert mixed or non-array tables: keys must be strings. Perhaps not recently, though. Why is that?

It seems that the methods that used to throw these errors have begun to put up with them. You can put together a very simple unit test and see that if you try to save a mixed table to a DataStore or pass it from server to client (or vice-versa), no error will be thrown, but the table will have dropped all its string-keyed values and anything past the first missing integer index. And without any warning, I might add.

That is, only if the table contains a value at index 1.

If, for example, we take the following table and pass it from server to client (as a return from a RemoteFunction invocation), something very bizarre happens:

local perpetrator = {[2] = true}

This little guy will wreak havoc in your codebase. Let’s check out what happens.

  1. The client prints the error Cannot convert mixed or non-array tables: keys must be strings (which, we can all agree, is an incredibly unspecific and unhelpful error message) and references the line where RemoteFunction:InvokeServer was called.
  2. The server prints… nothing. It acts as though no issue occurred on its end, which can lead its developer to think the error was only in client code.
  3. Every subsequent call to InvokeServer on this RemoteFunction REGARDLESS OF WHICH CLIENT calls it returns the same Cannot convert mixed or non-array tables: keys must be strings error. Yes, it breaks that RemoteFunction for all players. Again, it does this without printing a single message in Server log.

To be specific, here are the only steps needed to reproduce:

  1. Call RemoteFunction:InvokeServer from a client
  2. The server’s OnServerInvoke returns the above mentioned table

Here is a minimal repro file (17.6 KB) containing some helpful comments. It includes a button that you can press to initiate the errors so that you can test it from two clients – one presses the button and the other doesn’t, but they both suffer the consequences.

Resolutions:

  • The RemoteFunction should continue to operate as expected for all clients despite encountering the error
  • All mixed tables should throw an error, or none of them should.
  • The server log should display an error, and (bonus) an even more specific message about what was wrong with the table (i.e. it has string keys and a numerical key, or it has all numerical keys but one is missing, perhaps the depth in the table that this problem was encountered, the position in the tuple being returned [when multiple values are being returned by the function], etc).
    OR
    Stop using JSON (I assume that’s the reason for the limitations) to transfer/store tables and use something that supports native Lua tables. I realize that serializing Lua tables is not a complex issue, but the more complex issue here is modifying existing code and migrating existing DataStores. Even that doesn’t seem that complex to me, but maybe I’m missing something.
10 Likes

I reported this here:

Note the staff reply that might be of relevance here too:

6 Likes

Please reread the post carefully. The main issue I am reporting is not the truncation of the tables, but the RemoteFunction breaking for all clients when specific tables are passed.

Edit: unless you just meant in reply to that one quoted portion (in which case I am guilty of not reading carefully)

2 Likes

I agree that Lua table support could be expanded, but it’s unreasonable to fully support them. Tables could have userdata values from newproxy(true) that only exist on one end of the connection, for example. Also, Roblox currently doesn’t use JSON for RemoteFunctions, but instead a format in which tables can either be Dictionaries (mapping from string to any value) or Arrays.