While this looks handy, Iâm not sure itâs what Iâm looking for. It looks like it generates a traceback for the line debug.traceback
is called on so it canât be used to find the origin of an error caught by pcall.
Take this example:
function a()
if math.random(0, 1) == 1 then
error("error!")
else
error("error!")
end
end
function b()
local success, err = pcall(a)
if not success then warn("an error was thrown, but from where?", err) end
-- <really important code that needs to still run, even if a() fails>
end
b()
how do I tell which error() call is the one generating the failure? Calling debug.traceback
wouldnât really help because while in this example Iâm calling error(), what Iâm more interested in catching is real errors. Errors where I canât just call debug.traceback
on the line before the error because I donât know where the error is. If I didnât call function âaâ in a pcall then I would get an error with a traceback but b would also break. Calling function âaâ within a pcall means b will continue to run but I wonât get a traceback for the error within âaâ.
With the above example you get the following output where the line the error occurred on is mentioned in the err
string:
an error was thrown, but from where? Workspace.Script:5: error!
This doesnât work in all situations though. Some errors (ones not caused by error() or syntax errors, like teleportservice errors) donât have what line caused them in the title. Iâll just get an error like âfailed to teleport players because one or more players doesnât have a parentâ or something but I have a million different lines that could be coming from and it doesnât say in the âerrâ string what line it came from.
Hereâs a better example:
function c()
game:GetService("TeleportService"):TeleportAsync(233195390, {})
end
function d()
c()
-- <very important code that must run, even if c() fails>
end
d()
Function âcâ when called outright like this will give us an error like so:
20:57:55.502 Invalid list of players for teleport. - Server - Script:2
20:57:55.502 Stack Begin - Studio
20:57:55.502 Script âWorkspace.Scriptâ, Line 2 - function c - Studio - Script:2
20:57:55.502 Script âWorkspace.Scriptâ, Line 5 - function d - Studio - Script:5
20:57:55.502 Script âWorkspace.Scriptâ, Line 9 - Studio - Script:9
20:57:55.503 Stack End - Studio
This makes what line the error occurred on very clear but it also means that function d() will break on the line where it calls c() and the line after c() is called will never run. If we re-write function d() to include a pcall like soâŚ
function d()
local success, err = pcall(c)
if not success then
warn(err)
end
-- <very important code that must run, even if c() fails>
end
âŚfunction d() will get to finish, even if c() throws an error, but we wonât get any details about the error itself. This is what we will get in the output:
20:53:12.770 Invalid list of players for teleport. - Server - Script:7
Script:7 just points to the line warn() was called on, not where the actual error occurred within function c().
This is what I mean by pcall returning more values.
As things are, pcall returns the following:
1): A âsuccessâ boolean that will be true if function d() finished without an error, and will be false if function d() threw an error.
2a): If âsuccessâ is true, pcall will return all values returned by function d() (in this case none)
2b): If âsuccessâ is false, pcall will return a string containing the title of the error, but not the traceback.
What I propose is pcall should return a third value as so:
3): If âsuccessâ is false, pcall will return another string containing the traceback of the error.
How this would look in action:
function c()
game:GetService("TeleportService"):TeleportAsync(233195390, {})
end
function d()
local success, err, trace = pcall(c)
if not success then
warn(err, traceback)
end
-- <very important code that must run, even if c() fails>
end
d()
Output:
20:57:55.502 Invalid list of players for teleport. - Server - Script:2
20:57:55.502 Stack Begin - Studio
20:57:55.502 Script âWorkspace.Scriptâ, Line 2 - function c - Studio - Script:2
20:57:55.502 Script âWorkspace.Scriptâ, Line 5 - function d - Studio - Script:5
20:57:55.502 Script âWorkspace.Scriptâ, Line 9 - Studio - Script:9
20:57:55.503 Stack End - Studio
This way I could have the best of both worlds. I could allow function d() to complete without stopping for c()'s error, but still get the full details about c()'s error for debugging.
Hope this clears up what I meant. Sorry for any confusion.