Error propagation

classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

Error propagation

Soni "They/Them" L.
I noticed there doesn't seem to be any way to propagate errors "caught"
with pcall/xpcall/lua_pcall. Why is this the case?

Throwing an error from the error handler isn't an option, as Lua eats
those, and calling error() isn't an option as Lua eats the backtrace in
that case. It would be nice to see a return value used to indicate
whether to "keep unwinding" or just eat the error.[1][2]

Nested pcalls with "keep unwinding" set would give access to the full
backtrace to all pcalls. That is, if you have 3 propagating pcalls then
1 eating pcall, they'd all have their error handlers called before the
error unwinds the stack. Returning a new error object would use the new
error object for future error handler calls. Error handlers would be
called in reverse order. Erroring in non-propagating error handlers
would just, uh, so like there are 2 ways to do this: restart the error
handling from the top of the stack, or restart from the erroring error
handler. I'm not sure which one would be "more correct" but I'd probably
go with the latter.

Other languages don't do this - they print errors as soon as their
equivalent of error() is called.[3]

[1] https://www.lua.org/manual/5.3/manual.html#2.3

[2] https://www.lua.org/manual/5.3/manual.html#lua_pcall

[3] https://doc.rust-lang.org/std/panic/fn.set_hook.html

Reply | Threaded
Open this post in threaded view
|

Re: Error propagation

John Belmonte
The caller of pcall is responsible for propagating the errors however it sees fit (by raising an exception with error(), passing the error object around, or local handling).  The Lua core and standard library rarely raise exceptions themselves-- most errors are signaled by return arguments.  As far as the message handler of xpcall, it's not an error handler as you call it, but apparently a way to expand or translate the exception object in flight which will eventually be returned to the caller of xpcall.

Given your recent posts, I think you'd gain from the Lua Gems chapter I wrote, "Exceptions in Lua".  It covers error handling, why using inline functions as a poor-man's scope less than ideal, exception safety (i.e. finalization), and scope managers.  There's even a footnote that declares hooking of scope exit as the last fundamental building block missing from Lua :)


On Fri, Jul 20, 2018 at 7:22 PM, Soni "They/Them" L. <[hidden email]> wrote:
I noticed there doesn't seem to be any way to propagate errors "caught" with pcall/xpcall/lua_pcall. Why is this the case?

Throwing an error from the error handler isn't an option, as Lua eats those, and calling error() isn't an option as Lua eats the backtrace in that case. It would be nice to see a return value used to indicate whether to "keep unwinding" or just eat the error.[1][2]

Nested pcalls with "keep unwinding" set would give access to the full backtrace to all pcalls. That is, if you have 3 propagating pcalls then 1 eating pcall, they'd all have their error handlers called before the error unwinds the stack. Returning a new error object would use the new error object for future error handler calls. Error handlers would be called in reverse order. Erroring in non-propagating error handlers would just, uh, so like there are 2 ways to do this: restart the error handling from the top of the stack, or restart from the erroring error handler. I'm not sure which one would be "more correct" but I'd probably go with the latter.

Other languages don't do this - they print errors as soon as their equivalent of error() is called.[3]

[1] https://www.lua.org/manual/5.3/manual.html#2.3

[2] https://www.lua.org/manual/5.3/manual.html#lua_pcall

[3] https://doc.rust-lang.org/std/panic/fn.set_hook.html


Reply | Threaded
Open this post in threaded view
|

Re: Error propagation

kurapica
In reply to this post by Soni "They/Them" L.

 

There are two kind of the errors, one we trigger it by ourselves, another one is unexpected.

 

For the unexpected error, all we need is where it happened, we can use the `error(msg, 0)` to send the msg without change its error location, although the stacktrace info will be broken.

 

For the expected one, we have to deal with it, like log the error with trace back info, throw it to outside, replaced it with a new error message(like we call the function with wrong arguments).

 

To diff them, we can use exception object(table) for expected ones, since the unexpected errors are just strings. Here is an example from my PLoop lib, I use `throw` to generate an exception object and save the stack trace, so I can use error(ret) to continue throw it out or just handle it with exception information.

 

```

require "PLoop"

 

PLoop(function(_ENV) – just ignore this

            function a()

                        throw("some thing error")

            end

 

            local ok, ret = pcall(a)

            --some thing error

            --stack traceback:

            --        x:\WorkSpace\test.lua:5: in function <x:\WorkSpace\test.lua:4>

            print(ret.StackTrace)

end)

```

Reply | Threaded
Open this post in threaded view
|

Re: Error propagation

Roberto Ierusalimschy
In reply to this post by John Belmonte
> [...]  As
> far as the message handler of xpcall, it's not an error handler as you call
> it, but apparently a way to expand or translate the exception object in
> flight which will eventually be returned to the caller of xpcall.

The "error handler" has been renamed "message handler" in more recent
versions of the manual, to make that point clear.

-- Roberto