Unable to catch raw errors using pcall/xpcall
filiptibell opened this issue · 6 comments
mlua
wraps every error in a custom error userdata, which means using errors that are tables or something else does not work. Here is an example that works in luau but that does not work when using mlua:
local success, error = pcall(function()
error({ Foo = "Bar" })
end)
assert(not success)
assert(type(error) == "table")
assert(error.Foo == "Bar")
I'm not sure how standard lua handles this, but luau allows for custom error types and users of my tool expect them to work: lune-org/lune#22
Your example actually works in mlua:
lua.load(r#"
local success, error = pcall(function()
error({ Foo = "Bar" })
end)
assert(not success)
assert(type(error) == "table")
assert(error.Foo == "Bar")
"#)
.exec()
.unwrap();
does not cause any panics.
What likely can happen, is error passes Rust boundary, where mlua collects traceback and wraps it to a rust-friendly form (the Error
object). Propagating this error back to Lua and catching later on Lua side gives userdata object from the rust world.
It's not really a bug, rather a feature, as this is how it was designed. Possibly I can change the behaviour to proposed or provide a method on Lua side to downgrate the object to original (if any).
I need to think about it
What likely can happen, is error passes Rust boundary, where mlua collects traceback and wraps it to a rust-friendly form (the
Error
object). Propagating this error back to Lua and catching later on Lua side gives userdata object from the rust world.
That is probably what is happening, it makes sense to me given that we manually resume threads and throw errors using the luau interrupt - #252 (reply in thread)
If there is a method in the future to downgrade the error back to a pure lua error then I think that would be a great solution in this case
When looking at the API documentation, I was surprised that mlua::Error::RuntimeError
wrapped a std::string::String
instead of an mlua::Value
. I was expecting that mlua::Error::RuntimeError
would wrap an mlua::Value
instead, allowing all types to be caught and handled by Rust code easily. I would then expect this to be unwrapped and thrown upon entering Lua code again, so it could be caught by the Lua code as if it had unwound through the Rust code. It would also allow values to be idiomatically thrown by Rust. I'm curious about what the rationale was for the current design.
I was expecting that mlua::Error::RuntimeError would wrap an mlua::Value instead, allowing all types to be caught and handled by Rust code easily.
I'm curious about what the rationale was for the current design.
Unfortunately (given that the definition of Value is Value<'a>
) this would add lifetime to Error making everything way too complex (Error<'a>
🤦 ).
Also the current Error
struct does not have any Lua
dependency and can be nicely printed from any place in Rust. Plus this type (by default) includes a Lua stacktrace.
Ah, that makes sense.
In the current API, is there a way to do what I'm trying to do; namely, can I allow Lua errors to unwind (or appear to as though the function being called was a Lua one) through the Rust code somehow? I thought it would be as simple as appending ?
to all of my Lua calls, but if there is some other way of doing it currently (even if it is less idiomatic or ergonomic), I would love to hear it.
Regardless, I still think that an API such as the one I described in my earlier comment would be nice to have, although I now understand why it might not be desirable for all. Having a brief look at the source code (with no prior knowledge of either Lua's API or mlua's source), it seems like such an API could be implemented (although I really want to emphasise how little I know about all of the code involved here). Maybe there is some way to allow both options, such as if the type returned allowed the caller to either extract the original value or convert it into the lifetime-less version.
Finally, I don't see where the Lua stack trace is in mlua::Error
; perhaps I misunderstood you?
Just to provide an update on this - we've worked around this with a redesign on our end instead where we don't need to pass errors from lua -> rust -> back to lua anymore and it works great. Downcasting might be nice but was not necessary for us, we just needed a better design. Issue title and description is not really accurate either so I will close this.