Infinite yield on RemoteFunction:InvokeServer if server thread got yielded and resumed

A call to RemoteFunction:InvokeServer infinitely yields when the server thread that handled the request has been yielded and resumed. Of course, if the thread is yielded, the client would yield. But in this case, even when the thread is resumed and a result is returned, the client is still waiting when it should have gotten the result.

Here’s the server side:

script.Parent.RemoteFunction.OnServerInvoke = function()
	local thread = coroutine.running()
	task.defer(coroutine.resume, thread)
	coroutine.yield()
	
	print("SERVER RETURNED")
	return "Success"
end

And client side:

print("WAITING")

local ran, result = pcall(function()
	return script.Parent.RemoteFunction:InvokeServer()
end)

warn("RECIEVED:", result)

And the output:

(Client): WAITING
(Server): SERVER RETURNED

Here’s the reproduction file, which you should place in StarterGui: File.rbxm (2.8 KB)


It’s important to note that the use of task.wait and wait both yield, but the bug does NOT occur with these. It only occurs with coroutine.yield. That means that if the server code was:

script.Parent.RemoteFunction.OnServerInvoke = function()
	task.wait(2)
	
	print("SERVER RETURNED")
	return "Success"
end

Then the client WILL receive the result after 2 seconds. If you instead format it like this, which does the exact same thing in theory:

script.Parent.RemoteFunction.OnServerInvoke = function()
	local thread = coroutine.running()
	task.delay(2, coroutine.resume, thread)
	coroutine.yield()
	print("SERVER RETURNED")
	return "Success"
end

Then the client WILL NOT receive the result after 2 seconds. The server still prints SERVER RETURNED, but the client infinitely yields.


OS: Windows 11
VERSION: 0.629.0.6290609

Expected behavior

I expect RemoteFunction:InvokeServer to get the server’s result when it’s returned to the client, even if the thread was paused at some point.

4 Likes

Using :Wait() on RBXScriptSignals does NOT cause the issue, so it could act as a workaround for this issue in the mean time:

local event = Instance.new("BindableEvent")

script.Parent.RemoteFunction.OnServerInvoke = function()
	event.Event:Wait()
	
	print("SERVER RETURNED")
	return "Success"
end

while task.wait(4) do
	event:Fire()
end

It’s odd, because internally that method probably uses similar functionality to coroutine.yield(), in either C++ or Lua. Same for task.wait(). I wonder why it acts differently when you use coroutine.yield() directly?

I think this is a duplicate of Coroutine.resume() bug while used in RemoteFunction & BindableFunction

You are also able to get around this issue using task.spawn in place of coroutine.resume

This is just an acknowledgment announcement!

We’ve filed a ticket into our internal database for this issue and will start investigating, we will update you when we have further information.

Thanks for flagging!

2 Likes

When you manually resume a coroutine you bypass our engine’s resume implementation resulting in continuations not being processed. This means anything waiting on your script to return will wait forever. It’s not strictly a bug more of a side-effect of the way things are implemented. You can see my response to a similar issue here.

We are unlikely to ever fix this, but you can actually handle it yourself. You can use task.spawn as a drop in replacement for coroutine.resume and things will automatically work as you expect :smiley:

4 Likes