Non-yielding unperformant code in metamethod causes problems in ModuleScript

Export module is requiring APIService, which is then requiring a 6,050,000characters-long module script through GetDependencies, a script that returns metatable using the __index metamethod to change it’s functionality to requiring a script of the indexed name descendant of Root (in this case, the plugin’s main script). This leads to the following exceptions:

attempt to yield across metamethod/C-call boundary
ModuleScript cannot resume non-suspended awaiting coroutine

In certain conditions I don’t specifically know, the exception thrown was changed to:

cannot resume dead coroutine

which was misleading and caused me to delay the solving of the problem an hour or so.

Export is subsequently requested by the Plugin’s main script, ScriptSynchronizer, and that’s stated as the most recent error traceback call line. Instead, if the metamethod plainly returns the instance of the ModuleScript, even if that same ModuleScript is required afterwards outside the metamethod, no problem arises. Note that both ScriptSynchronizer has to manually require Export, and Export has to manually require APIService in order for the error to be solved; APIService doesn’t use GetDependencies and therefore never caused any issue.

Expected behavior

This might be expected behaviour, but it’d be nice if the errors were more descriptive or specific, if the documentation explained the timeout time of required scripts within metamethods (which I’m assuming is one heartbeat), and then even if this timeout was customizable.

1 Like

The error is for when you yield, not for when you hit some kind of timeout. Yielding functions require special code by the engine/Luau in order to operate correctly, you are unable to call them in metamethods due to “significant performance implications”.

Yielding functions (such as task.wait or :WaitForChild) usually have the Can Yield tag on the Documentation:

image

I believe a workaround is to put the require inside of a coroutine.wrap/task.spawn or similar, it should allow your modules to yield when being require’d.

Something that Roblox could look into is improving the error messages, such as the attempt to yield across one to point to the function itself which is inside the Module, instead of just the require which eventually calls it.

1 Like