PyO3/pyo3 · error
Unexpected type in `__mro__` attribute.
Error message
Unexpected type in `__mro__` attribute.
What it means
The sibling panic to error 101 in `PyTypeMethods::mro()`: under the limited API or PyPy, pyo3 fetches `__mro__` via `getattr` and then `.extract::<Bound<PyTuple>>()`. This `.expect` fires when `__mro__` exists but is not a tuple of type objects, so extraction fails.
Source
Thrown at src/types/typeobject.rs:223
/// Checks whether `self` is a subclass of type `T`.
///
/// Equivalent to the Python expression `issubclass(self, T)`, if the type
/// `T` is known at compile time.
fn is_subclass_of<T>(&self) -> PyResult<bool>
where
T: PyTypeInfo,
{
self.is_subclass(&T::type_object(self.py()))
}
fn mro(&self) -> Bound<'py, PyTuple> {
#[cfg(any(Py_LIMITED_API, PyPy))]
let mro = self
.getattr(intern!(self.py(), "__mro__"))
.expect("Cannot get `__mro__` from object.")
.extract()
.expect("Unexpected type in `__mro__` attribute.");
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
let mro = unsafe {
use crate::ffi_ptr_ext::FfiPtrExt;
(*self.as_type_ptr())
.tp_mro
.assume_borrowed(self.py())
.to_owned()
.cast_into_unchecked()
};
mro
}
fn bases(&self) -> Bound<'py, PyTuple> {
#[cfg(any(Py_LIMITED_API, PyPy))]
let bases = self
.getattr(intern!(self.py(), "__bases__"))View on GitHub (pinned to ac9b6899d3)
Solutions
- Ensure the wrapped object is a real type and nothing shadows `__mro__` with a non-tuple
- Extract manually with graceful handling: `getattr("__mro__")` then `downcast::<PyTuple>()` and handle the error
- Remove or fix custom `__mro__` overrides on the class
Example fix
// before
let mro = ty.mro(); // panics if __mro__ is not a tuple
// after
let mro = ty.getattr("__mro__")?.downcast_into::<PyTuple>().ok(); Defensive patterns
Strategy: type-guard
Validate before calling
let ok = ty.getattr("__mro__").map(|a| a.is_instance_of::<PyTuple>()).unwrap_or(false);
if !ok { return Err("object has no tuple __mro__"); } Type guard
fn has_tuple_mro(ty: &Bound<'_, PyAny>) -> bool {
ty.getattr("__mro__").map(|a| a.downcast::<PyTuple>().is_ok()).unwrap_or(false)
} Try / catch
let mro = ty.getattr("__mro__")
.and_then(|a| a.downcast_into::<PyTuple>())
.map_err(|e| format!("malformed __mro__: {e}"))?; Prevention
- Avoid metaclasses that override `__mro__` with non-tuple values
- Validate third-party classes before reflecting on them
- Prefer the non-limited-API build on CPython where `tp_mro` is read directly
When it happens
Trigger: Calling `.mro()` on a type-like object whose `__mro__` attribute is present but has a non-tuple value (custom metaclass or shadowing attribute) under `Py_LIMITED_API` or PyPy.
Common situations: Custom metaclasses or monkey-patched classes that override `__mro__` with something unexpected; unusual embedding environments where the 'type' wrapper actually wraps a non-type object with a `__mro__`-like attribute.
Related errors
- Cannot get `__mro__` from object.
- Cannot get `__bases__` from object.
- Unexpected type in `__bases__` attribute.
- Neither abi3 or abi3t features are enabled
- Cannot target an abi3t version below {MINIMUM_SUPPORTED_VERS
AI-assisted analysis of PyO3/pyo3@ac9b6899d3 (2026-09-05).
Data as JSON: /api/errors/3ba9a7ada9aaf753.
Report an issue: GitHub.