Wrap error without losing call stack?

Hello everyone, I recently came into a problem. I am making an async event function and what it does is call a listener and wrap its error to see if it errored:

task.spawn(function()
	local _, errMsg = pcall(function() listener(table.unpack(arguments)) end);
	done += 1;
	...
end);

However, I noticed that doing so will return very poor call stacks that result almost impossible to debug in an extensive system. The only way I made the call stack appear was without wrapping the function however this will make so that it yields forever since “done” is never updated after the listener is called.

Without pcall (Good):
image

With pcall (Bad):
image

The reason I am blurring the name is that it is my friend that encountered this problem with the module and asked me to not show his username

1 Like

Errors propogate to calls to function values returned by coroutine.wrap().

1 Like

I can’t wrap the listener since it would also make it asynchronous which means I wouldn’t be able to yield until the function finished.

1 Like

Note: The problem isn’t coroutine.wrap or task.spawn. The problem is that pcall doesn’t return the call stack.

1 Like

You can yield inside of the function body of a call to coroutine.wrap() via coroutine.yield() unless by yield you were referring to a timed delay.

1 Like

That’s because pcall calls functions in an alternate function call stack.

I was thinking about that however it would never un-yield since it would error, and we wouldn’t know if it errored or not:

local thread = coroutine.running();
coroutine.wrap(function()
	listener();
	coroutine.resume(thread);
end)();

coroutine.yield(thread);
1 Like

Is there a solution to this?

Don’t call the function in protected mode.
done += 1 will only fail to execute if the ‘listener’ function errors terminating the function call stack.

1 Like

The thing is doing so would make it always yield. I am making a promise where you can yield until all events finished. This is why there is done += 1. If the function errors we will never know if it finished or if it errored.

For instance let’s say the listener was this:

myEmitter:On("test"):Connect(function()
	task.wait(5);

	error("Couldn't finish");
end);

The end goal is that it yields for 5 seconds and then errors with the desired call stack.

So emitting would be:

local Promise = myEmitter:EmitAsync("test");
warn("It is async!");
Promise:Yield();
warn("This will print but it errors in a separate thread. Therefore, the script continues executing");
1 Like