{"record":{"id":"aa25567cde8f60ad","repo":"PyO3/pyo3","slug":"cannot-get-mro-from-object","errorCode":null,"errorMessage":"Cannot get `__mro__` from object.","messagePattern":"Cannot get `__mro__` from object\\.","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/types/typeobject.rs","lineNumber":221,"sourceCode":"        Ok(result == 1)\n    }\n\n    /// Checks whether `self` is a subclass of type `T`.\n    ///\n    /// Equivalent to the Python expression `issubclass(self, T)`, if the type\n    /// `T` is known at compile time.\n    fn is_subclass_of<T>(&self) -> PyResult<bool>\n    where\n        T: PyTypeInfo,\n    {\n        self.is_subclass(&T::type_object(self.py()))\n    }\n\n    fn mro(&self) -> Bound<'py, PyTuple> {\n        #[cfg(any(Py_LIMITED_API, PyPy))]\n        let mro = self\n            .getattr(intern!(self.py(), \"__mro__\"))\n            .expect(\"Cannot get `__mro__` from object.\")\n            .extract()\n            .expect(\"Unexpected type in `__mro__` attribute.\");\n\n        #[cfg(not(any(Py_LIMITED_API, PyPy)))]\n        let mro = unsafe {\n            use crate::ffi_ptr_ext::FfiPtrExt;\n            (*self.as_type_ptr())\n                .tp_mro\n                .assume_borrowed(self.py())\n                .to_owned()\n                .cast_into_unchecked()\n        };\n\n        mro\n    }\n\n    fn bases(&self) -> Bound<'py, PyTuple> {\n        #[cfg(any(Py_LIMITED_API, PyPy))]","sourceCodeStart":203,"sourceCodeEnd":239,"githubUrl":"https://github.com/PyO3/pyo3/blob/ac9b6899d348be4d54614d060dea53a645a12e36/src/types/typeobject.rs#L203-L239","documentation":"pyo3's `PyTypeMethods::mro()` retrieves the type's method resolution order. Under the limited API or PyPy, where `tp_mro` is not directly accessible, it falls back to reading the `__mro__` attribute via `getattr` and panics with this message if that attribute cannot be fetched. This happens when the object is not a real Python type exposing `__mro__`.","triggerScenarios":"Calling `.mro()` on a `PyTypeMethods` receiver built from a type pointer that has no `__mro__` attribute, while compiled with `Py_LIMITED_API` or on PyPy (the `tp_mro` fast path is cfg'd out).","commonSituations":"Running under PyPy or a limited-API/abi3 build where code that worked on CPython (direct `tp_mro` access) now goes through `getattr(\"__mro__\")` and the wrapped object is not a proper type object.","solutions":["Ensure the object is a genuine Python type (`type(x)`, heap type, etc.) before calling `.mro()`","Guard with a check that `__mro__` exists, or handle it via `getattr_opt`-style lookup instead","Build against CPython without Py_LIMITED_API to use the direct `tp_mro` path"],"exampleFix":"// before\nlet mro = ty.mro(); // panics if __mro__ missing under limited API/PyPy\n// after\nlet mro = ty.getattr(\"__mro__\").ok()\n    .map(|a| a.extract::<Bound<PyTuple>>().expect(\"__mro__ not a tuple\"));","handlingStrategy":"type-guard","validationCode":"// Python-side or Rust-side check before calling mro()\nif !obj.is_instance_of::<crate::types::PyType>() {\n    return Err(\"mro() requires a real Python type\");\n}","typeGuard":"fn is_real_type(obj: &Bound<'_, PyAny>) -> bool {\n    obj.downcast::<crate::types::PyType>().is_ok()\n}","tryCatchPattern":"// use non-panicking access instead\nlet mro = ty.getattr(\"__mro__\")\n    .and_then(|a| a.downcast_into::<PyTuple>())\n    .map_err(|e| format!(\"no usable __mro__: {e}\"))?;","preventionTips":["Only call `.mro()` on objects obtained via `PyType` / `type(ptr)` paths","Test abi3/PyPy configurations in CI — attribute fallbacks only run there","Prefer `downcast`/`getattr` APIs over `.expect`-based accessors in user code"],"tags":["rust","pyo3","pypy","limited-api","types"],"backgroundTag":"missing-dunder-attribute","analyzedSha":"ac9b6899d348be4d54614d060dea53a645a12e36","analyzedAt":"2026-09-05T09:20:35.319Z","contentChangedAt":"2026-09-05T09:20:35.319Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}