mlua/
error.rs

1use std::error::Error as StdError;
2use std::fmt;
3use std::io::Error as IoError;
4use std::net::AddrParseError;
5use std::result::Result as StdResult;
6use std::str::Utf8Error;
7use std::string::String as StdString;
8use std::sync::Arc;
9
10use crate::private::Sealed;
11
12#[cfg(feature = "error-send")]
13type DynStdError = dyn StdError + Send + Sync;
14
15#[cfg(not(feature = "error-send"))]
16type DynStdError = dyn StdError;
17
18/// Error type returned by `mlua` methods.
19#[derive(Debug, Clone)]
20#[non_exhaustive]
21pub enum Error {
22    /// Syntax error while parsing Lua source code.
23    SyntaxError {
24        /// The error message as returned by Lua.
25        message: StdString,
26        /// `true` if the error can likely be fixed by appending more input to the source code.
27        ///
28        /// This is useful for implementing REPLs as they can query the user for more input if this
29        /// is set.
30        incomplete_input: bool,
31    },
32    /// Lua runtime error, aka `LUA_ERRRUN`.
33    ///
34    /// The Lua VM returns this error when a builtin operation is performed on incompatible types.
35    /// Among other things, this includes invoking operators on wrong types (such as calling or
36    /// indexing a `nil` value).
37    RuntimeError(StdString),
38    /// Lua memory error, aka `LUA_ERRMEM`
39    ///
40    /// The Lua VM returns this error when the allocator does not return the requested memory, aka
41    /// it is an out-of-memory error.
42    MemoryError(StdString),
43    /// Lua garbage collector error, aka `LUA_ERRGCMM`.
44    ///
45    /// The Lua VM returns this error when there is an error running a `__gc` metamethod.
46    #[cfg(any(feature = "lua53", feature = "lua52", doc))]
47    #[cfg_attr(docsrs, doc(cfg(any(feature = "lua53", feature = "lua52"))))]
48    GarbageCollectorError(StdString),
49    /// Potentially unsafe action in safe mode.
50    SafetyError(StdString),
51    /// Memory control is not available.
52    ///
53    /// This error can only happen when Lua state was not created by us and does not have the
54    /// custom allocator attached.
55    MemoryControlNotAvailable,
56    /// A mutable callback has triggered Lua code that has called the same mutable callback again.
57    ///
58    /// This is an error because a mutable callback can only be borrowed mutably once.
59    RecursiveMutCallback,
60    /// Either a callback or a userdata method has been called, but the callback or userdata has
61    /// been destructed.
62    ///
63    /// This can happen either due to to being destructed in a previous __gc, or due to being
64    /// destructed from exiting a `Lua::scope` call.
65    CallbackDestructed,
66    /// Not enough stack space to place arguments to Lua functions or return values from callbacks.
67    ///
68    /// Due to the way `mlua` works, it should not be directly possible to run out of stack space
69    /// during normal use. The only way that this error can be triggered is if a `Function` is
70    /// called with a huge number of arguments, or a Rust callback returns a huge number of return
71    /// values.
72    StackError,
73    /// Too many arguments to [`Function::bind`].
74    ///
75    /// [`Function::bind`]: crate::Function::bind
76    BindError,
77    /// Bad argument received from Lua (usually when calling a function).
78    ///
79    /// This error can help to identify the argument that caused the error
80    /// (which is stored in the corresponding field).
81    BadArgument {
82        /// Function that was called.
83        to: Option<StdString>,
84        /// Argument position (usually starts from 1).
85        pos: usize,
86        /// Argument name.
87        name: Option<StdString>,
88        /// Underlying error returned when converting argument to a Lua value.
89        cause: Arc<Error>,
90    },
91    /// A Rust value could not be converted to a Lua value.
92    ToLuaConversionError {
93        /// Name of the Rust type that could not be converted.
94        from: String,
95        /// Name of the Lua type that could not be created.
96        to: &'static str,
97        /// A message indicating why the conversion failed in more detail.
98        message: Option<StdString>,
99    },
100    /// A Lua value could not be converted to the expected Rust type.
101    FromLuaConversionError {
102        /// Name of the Lua type that could not be converted.
103        from: &'static str,
104        /// Name of the Rust type that could not be created.
105        to: String,
106        /// A string containing more detailed error information.
107        message: Option<StdString>,
108    },
109    /// [`Thread::resume`] was called on an unresumable coroutine.
110    ///
111    /// A coroutine is unresumable if its main function has returned or if an error has occurred
112    /// inside the coroutine. Already running coroutines are also marked as unresumable.
113    ///
114    /// [`Thread::status`] can be used to check if the coroutine can be resumed without causing this
115    /// error.
116    ///
117    /// [`Thread::resume`]: crate::Thread::resume
118    /// [`Thread::status`]: crate::Thread::status
119    CoroutineUnresumable,
120    /// An [`AnyUserData`] is not the expected type in a borrow.
121    ///
122    /// This error can only happen when manually using [`AnyUserData`], or when implementing
123    /// metamethods for binary operators. Refer to the documentation of [`UserDataMethods`] for
124    /// details.
125    ///
126    /// [`AnyUserData`]: crate::AnyUserData
127    /// [`UserDataMethods`]: crate::UserDataMethods
128    UserDataTypeMismatch,
129    /// An [`AnyUserData`] borrow failed because it has been destructed.
130    ///
131    /// This error can happen either due to to being destructed in a previous __gc, or due to being
132    /// destructed from exiting a `Lua::scope` call.
133    ///
134    /// [`AnyUserData`]: crate::AnyUserData
135    UserDataDestructed,
136    /// An [`AnyUserData`] immutable borrow failed.
137    ///
138    /// This error can occur when a method on a [`UserData`] type calls back into Lua, which then
139    /// tries to call a method on the same [`UserData`] type. Consider restructuring your API to
140    /// prevent these errors.
141    ///
142    /// [`AnyUserData`]: crate::AnyUserData
143    /// [`UserData`]: crate::UserData
144    UserDataBorrowError,
145    /// An [`AnyUserData`] mutable borrow failed.
146    ///
147    /// This error can occur when a method on a [`UserData`] type calls back into Lua, which then
148    /// tries to call a method on the same [`UserData`] type. Consider restructuring your API to
149    /// prevent these errors.
150    ///
151    /// [`AnyUserData`]: crate::AnyUserData
152    /// [`UserData`]: crate::UserData
153    UserDataBorrowMutError,
154    /// A [`MetaMethod`] operation is restricted (typically for `__gc` or `__metatable`).
155    ///
156    /// [`MetaMethod`]: crate::MetaMethod
157    MetaMethodRestricted(StdString),
158    /// A [`MetaMethod`] (eg. `__index` or `__newindex`) has invalid type.
159    ///
160    /// [`MetaMethod`]: crate::MetaMethod
161    MetaMethodTypeError {
162        /// Name of the metamethod.
163        method: StdString,
164        /// Passed value type.
165        type_name: &'static str,
166        /// A string containing more detailed error information.
167        message: Option<StdString>,
168    },
169    /// A [`RegistryKey`] produced from a different Lua state was used.
170    ///
171    /// [`RegistryKey`]: crate::RegistryKey
172    MismatchedRegistryKey,
173    /// A Rust callback returned `Err`, raising the contained `Error` as a Lua error.
174    CallbackError {
175        /// Lua call stack backtrace.
176        traceback: StdString,
177        /// Original error returned by the Rust code.
178        cause: Arc<Error>,
179    },
180    /// A Rust panic that was previously resumed, returned again.
181    ///
182    /// This error can occur only when a Rust panic resumed previously was recovered
183    /// and returned again.
184    PreviouslyResumedPanic,
185    /// Serialization error.
186    #[cfg(feature = "serde")]
187    #[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
188    SerializeError(StdString),
189    /// Deserialization error.
190    #[cfg(feature = "serde")]
191    #[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
192    DeserializeError(StdString),
193    /// A custom error.
194    ///
195    /// This can be used for returning user-defined errors from callbacks.
196    ///
197    /// Returning `Err(ExternalError(...))` from a Rust callback will raise the error as a Lua
198    /// error. The Rust code that originally invoked the Lua code then receives a `CallbackError`,
199    /// from which the original error (and a stack traceback) can be recovered.
200    ExternalError(Arc<DynStdError>),
201    /// An error with additional context.
202    WithContext {
203        /// A string containing additional context.
204        context: StdString,
205        /// Underlying error.
206        cause: Arc<Error>,
207    },
208}
209
210/// A specialized `Result` type used by `mlua`'s API.
211pub type Result<T> = StdResult<T, Error>;
212
213#[cfg(not(tarpaulin_include))]
214impl fmt::Display for Error {
215    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
216        match self {
217            Error::SyntaxError { message, .. } => write!(fmt, "syntax error: {message}"),
218            Error::RuntimeError(msg) => write!(fmt, "runtime error: {msg}"),
219            Error::MemoryError(msg) => {
220                write!(fmt, "memory error: {msg}")
221            }
222            #[cfg(any(feature = "lua53", feature = "lua52"))]
223            Error::GarbageCollectorError(msg) => {
224                write!(fmt, "garbage collector error: {msg}")
225            }
226            Error::SafetyError(msg) => {
227                write!(fmt, "safety error: {msg}")
228            },
229            Error::MemoryControlNotAvailable => {
230                write!(fmt, "memory control is not available")
231            }
232            Error::RecursiveMutCallback => write!(fmt, "mutable callback called recursively"),
233            Error::CallbackDestructed => write!(
234                fmt,
235                "a destructed callback or destructed userdata method was called"
236            ),
237            Error::StackError => write!(
238                fmt,
239                "out of Lua stack, too many arguments to a Lua function or too many return values from a callback"
240            ),
241            Error::BindError => write!(
242                fmt,
243                "too many arguments to Function::bind"
244            ),
245            Error::BadArgument { to, pos, name, cause } => {
246                if let Some(name) = name {
247                    write!(fmt, "bad argument `{name}`")?;
248                } else {
249                    write!(fmt, "bad argument #{pos}")?;
250                }
251                if let Some(to) = to {
252                    write!(fmt, " to `{to}`")?;
253                }
254                write!(fmt, ": {cause}")
255            },
256            Error::ToLuaConversionError { from, to, message } => {
257                write!(fmt, "error converting {from} to Lua {to}")?;
258                match message {
259                    None => Ok(()),
260                    Some(message) => write!(fmt, " ({message})"),
261                }
262            }
263            Error::FromLuaConversionError { from, to, message } => {
264                write!(fmt, "error converting Lua {from} to {to}")?;
265                match message {
266                    None => Ok(()),
267                    Some(message) => write!(fmt, " ({message})"),
268                }
269            }
270            Error::CoroutineUnresumable => write!(fmt, "coroutine is non-resumable"),
271            Error::UserDataTypeMismatch => write!(fmt, "userdata is not expected type"),
272            Error::UserDataDestructed => write!(fmt, "userdata has been destructed"),
273            Error::UserDataBorrowError => write!(fmt, "error borrowing userdata"),
274            Error::UserDataBorrowMutError => write!(fmt, "error mutably borrowing userdata"),
275            Error::MetaMethodRestricted(method) => write!(fmt, "metamethod {method} is restricted"),
276            Error::MetaMethodTypeError { method, type_name, message } => {
277                write!(fmt, "metamethod {method} has unsupported type {type_name}")?;
278                match message {
279                    None => Ok(()),
280                    Some(message) => write!(fmt, " ({message})"),
281                }
282            }
283            Error::MismatchedRegistryKey => {
284                write!(fmt, "RegistryKey used from different Lua state")
285            }
286            Error::CallbackError { cause, traceback } => {
287                // Trace errors down to the root
288                let (mut cause, mut full_traceback) = (cause, None);
289                while let Error::CallbackError { cause: cause2, traceback: traceback2 } = &**cause {
290                    cause = cause2;
291                    full_traceback = Some(traceback2);
292                }
293                writeln!(fmt, "{cause}")?;
294                if let Some(full_traceback) = full_traceback {
295                    let traceback = traceback.trim_start_matches("stack traceback:");
296                    let traceback = traceback.trim_start().trim_end();
297                    // Try to find local traceback within the full traceback
298                    if let Some(pos) = full_traceback.find(traceback) {
299                        write!(fmt, "{}", &full_traceback[..pos])?;
300                        writeln!(fmt, ">{}", &full_traceback[pos..].trim_end())?;
301                    } else {
302                        writeln!(fmt, "{}", full_traceback.trim_end())?;
303                    }
304                } else {
305                    writeln!(fmt, "{}", traceback.trim_end())?;
306                }
307                Ok(())
308            }
309            Error::PreviouslyResumedPanic => {
310                write!(fmt, "previously resumed panic returned again")
311            }
312            #[cfg(feature = "serde")]
313            Error::SerializeError(err) => {
314                write!(fmt, "serialize error: {err}")
315            },
316            #[cfg(feature = "serde")]
317            Error::DeserializeError(err) => {
318                write!(fmt, "deserialize error: {err}")
319            },
320            Error::ExternalError(err) => err.fmt(fmt),
321            Error::WithContext { context, cause } => {
322                writeln!(fmt, "{context}")?;
323                write!(fmt, "{cause}")
324            }
325        }
326    }
327}
328
329impl StdError for Error {
330    fn source(&self) -> Option<&(dyn StdError + 'static)> {
331        match self {
332            // An error type with a source error should either return that error via source or
333            // include that source's error message in its own Display output, but never both.
334            // https://blog.rust-lang.org/inside-rust/2021/07/01/What-the-error-handling-project-group-is-working-towards.html
335            // Given that we include source to fmt::Display implementation for `CallbackError`, this call
336            // returns nothing.
337            Error::CallbackError { .. } => None,
338            Error::ExternalError(err) => err.source(),
339            Error::WithContext { cause, .. } => Self::source(cause),
340            _ => None,
341        }
342    }
343}
344
345impl Error {
346    /// Creates a new `RuntimeError` with the given message.
347    #[inline]
348    pub fn runtime<S: fmt::Display>(message: S) -> Self {
349        Error::RuntimeError(message.to_string())
350    }
351
352    /// Wraps an external error object.
353    #[inline]
354    pub fn external<T: Into<Box<DynStdError>>>(err: T) -> Self {
355        Error::ExternalError(err.into().into())
356    }
357
358    /// Attempts to downcast the external error object to a concrete type by reference.
359    pub fn downcast_ref<T>(&self) -> Option<&T>
360    where
361        T: StdError + 'static,
362    {
363        match self {
364            Error::ExternalError(err) => err.downcast_ref(),
365            Error::WithContext { cause, .. } => Self::downcast_ref(cause),
366            _ => None,
367        }
368    }
369
370    /// An iterator over the chain of nested errors wrapped by this Error.
371    pub fn chain(&self) -> impl Iterator<Item = &(dyn StdError + 'static)> {
372        Chain {
373            root: self,
374            current: None,
375        }
376    }
377
378    /// Returns the parent of this error.
379    #[doc(hidden)]
380    pub fn parent(&self) -> Option<&Error> {
381        match self {
382            Error::CallbackError { cause, .. } => Some(cause.as_ref()),
383            Error::WithContext { cause, .. } => Some(cause.as_ref()),
384            _ => None,
385        }
386    }
387
388    pub(crate) fn bad_self_argument(to: &str, cause: Error) -> Self {
389        Error::BadArgument {
390            to: Some(to.to_string()),
391            pos: 1,
392            name: Some("self".to_string()),
393            cause: Arc::new(cause),
394        }
395    }
396
397    pub(crate) fn from_lua_conversion(
398        from: &'static str,
399        to: impl ToString,
400        message: impl Into<Option<String>>,
401    ) -> Self {
402        Error::FromLuaConversionError {
403            from,
404            to: to.to_string(),
405            message: message.into(),
406        }
407    }
408}
409
410/// Trait for converting [`std::error::Error`] into Lua [`Error`].
411pub trait ExternalError {
412    fn into_lua_err(self) -> Error;
413}
414
415impl<E: Into<Box<DynStdError>>> ExternalError for E {
416    fn into_lua_err(self) -> Error {
417        Error::external(self)
418    }
419}
420
421/// Trait for converting [`std::result::Result`] into Lua [`Result`].
422pub trait ExternalResult<T> {
423    fn into_lua_err(self) -> Result<T>;
424}
425
426impl<T, E> ExternalResult<T> for StdResult<T, E>
427where
428    E: ExternalError,
429{
430    fn into_lua_err(self) -> Result<T> {
431        self.map_err(|e| e.into_lua_err())
432    }
433}
434
435/// Provides the `context` method for [`Error`] and `Result<T, Error>`.
436pub trait ErrorContext: Sealed {
437    /// Wraps the error value with additional context.
438    fn context<C: fmt::Display>(self, context: C) -> Self;
439
440    /// Wrap the error value with additional context that is evaluated lazily
441    /// only once an error does occur.
442    fn with_context<C: fmt::Display>(self, f: impl FnOnce(&Error) -> C) -> Self;
443}
444
445impl ErrorContext for Error {
446    fn context<C: fmt::Display>(self, context: C) -> Self {
447        let context = context.to_string();
448        match self {
449            Error::WithContext { cause, .. } => Error::WithContext { context, cause },
450            _ => Error::WithContext {
451                context,
452                cause: Arc::new(self),
453            },
454        }
455    }
456
457    fn with_context<C: fmt::Display>(self, f: impl FnOnce(&Error) -> C) -> Self {
458        let context = f(&self).to_string();
459        match self {
460            Error::WithContext { cause, .. } => Error::WithContext { context, cause },
461            _ => Error::WithContext {
462                context,
463                cause: Arc::new(self),
464            },
465        }
466    }
467}
468
469impl<T> ErrorContext for Result<T> {
470    fn context<C: fmt::Display>(self, context: C) -> Self {
471        self.map_err(|err| err.context(context))
472    }
473
474    fn with_context<C: fmt::Display>(self, f: impl FnOnce(&Error) -> C) -> Self {
475        self.map_err(|err| err.with_context(f))
476    }
477}
478
479impl From<AddrParseError> for Error {
480    fn from(err: AddrParseError) -> Self {
481        Error::external(err)
482    }
483}
484
485impl From<IoError> for Error {
486    fn from(err: IoError) -> Self {
487        Error::external(err)
488    }
489}
490
491impl From<Utf8Error> for Error {
492    fn from(err: Utf8Error) -> Self {
493        Error::external(err)
494    }
495}
496
497#[cfg(feature = "serde")]
498impl serde::ser::Error for Error {
499    fn custom<T: fmt::Display>(msg: T) -> Self {
500        Self::SerializeError(msg.to_string())
501    }
502}
503
504#[cfg(feature = "serde")]
505impl serde::de::Error for Error {
506    fn custom<T: fmt::Display>(msg: T) -> Self {
507        Self::DeserializeError(msg.to_string())
508    }
509}
510
511#[cfg(feature = "anyhow")]
512impl From<anyhow::Error> for Error {
513    fn from(err: anyhow::Error) -> Self {
514        match err.downcast::<Self>() {
515            Ok(err) => err,
516            Err(err) => Error::external(err),
517        }
518    }
519}
520
521struct Chain<'a> {
522    root: &'a Error,
523    current: Option<&'a (dyn StdError + 'static)>,
524}
525
526impl<'a> Iterator for Chain<'a> {
527    type Item = &'a (dyn StdError + 'static);
528
529    fn next(&mut self) -> Option<Self::Item> {
530        loop {
531            let error: Option<&dyn StdError> = match self.current {
532                None => {
533                    self.current = Some(self.root);
534                    self.current
535                }
536                Some(current) => match current.downcast_ref::<Error>()? {
537                    Error::BadArgument { cause, .. }
538                    | Error::CallbackError { cause, .. }
539                    | Error::WithContext { cause, .. } => {
540                        self.current = Some(&**cause);
541                        self.current
542                    }
543                    Error::ExternalError(err) => {
544                        self.current = Some(&**err);
545                        self.current
546                    }
547                    _ => None,
548                },
549            };
550
551            // Skip `ExternalError` as it only wraps the underlying error
552            // without meaningful context
553            if let Some(Error::ExternalError(_)) = error?.downcast_ref::<Error>() {
554                continue;
555            }
556
557            return self.current;
558        }
559    }
560}
561
562#[cfg(test)]
563mod assertions {
564    use super::*;
565
566    #[cfg(not(feature = "error-send"))]
567    static_assertions::assert_not_impl_any!(Error: Send, Sync);
568    #[cfg(feature = "send")]
569    static_assertions::assert_impl_all!(Error: Send, Sync);
570}