Userdata instances created from newproxy() cannot be passed as arguments in BindableEvent:Fire()

Reproduction Steps

Code to reproduce issue:

local userdata = newproxy(true)

local bindableEvent = Instance.new("BindableEvent")

bindableEvent.Event:Connect(function(...)
    print("Fired with args:", ...) -- Prints "Hello" nil
end)

bindableEvent:Fire("Hello", userdata)

Expected Behavior

Passing a userdata created by newproxy() through a BindableEvent:Fire() should allow me to receive it as an argument in .Event:Connect()

Actual Behavior

Passing the result of newproxy() through :Fire() results in a nil argument when received through :Connect()

Workaround

The current workaround is just using a standard empty Lua table instead.

Issue Area: Engine
Issue Type: Other
Impact: Low
Frequency: Constantly
Date First Experienced: 2023-02-20 00:02:00 (-06:00)
Date Last Experienced: 2023-02-20 04:02:00 (-06:00)

Only Roblox userdata objects are allowed in arguments to events, as described in “Parameter Limitations” at BindableEvent | Roblox Creator Documentation
Maybe it could’ve been more explicit to say that userdata created in Luau isn’t allowed.

The parameter limitations described in the website seems to direct it’s focus to RemoteEvent and RemoteFunction:

Any type of Roblox object such as an Enum, Instance, or userdata can be passed as a parameter when a RemoteEvent is fired or a RemoteFunction invoked. Lua types such as numbers, strings, and booleans can also be passed, although there are some limitations on how data can be passed.

I also noticed that coroutines cannot be passed to it. It’s crazy how I didn’t know about this until now.

Why do BindableEvents/BindableFunctions have these behaviors?

  • It is clear that you are not doing anything related to network with these two objects.
  • There is no need to create full new tables when passing one.
  • Custom userdatas and coroutines should be passed with no problems, don’t understand why they are excluded.
  • They still have the bug where if you use coroutine.resume to resume the current yielding thread it makes the thread who called Invoke wait forever even though the callback finished and even returned. (This issue can be solved by using task.spawn, but why?)

My only guesses for the first three points is that because they are used for communication between CoreScripts and Normal Scripts sometimes, Roblox decided to have these points as a feature to avoid filtering on the CoreScript side which is written in Luau.

My second guess is that engine does this for an unknown reason that I want to know…

The bug of the fourth point is shown here as code form:

2 Likes

I’ll let the documentation team know that text needs to reference all four classes.
I see that Argument Limitations for Bindables and Remotes | Roblox Creator Documentation also doesn’t list all 4 classes.

To match RemoteEvent/RemoteFunction. Big differences between local and remote would be surprising.

1 Like

I believe the documentation here should be “Only Roblox objects are allowed, Lua userdata objects created through newproxy are not allowed”

Feel free to rework the wording on that.

To @focasds : The mutation behaviour with tables actually happens because bindables can cross what I like to refer to as “VM stacks” (game script, corescript, plugins, so on), there be dragons with letting two or more memory isolated stacks access the same table

We have updated the documentation around bindables, remotes and their parameter limitations.

4 Likes

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