{"record":{"id":"caea33d0d0a085b0","repo":"PyO3/pyo3","slug":"this-object-is-already-borrowed-caea33","errorCode":null,"errorMessage":"this object is already borrowed","messagePattern":"this object is already borrowed","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/pyclass/guard.rs","lineNumber":276,"sourceCode":"        let t_not_frozen = !<T::Frozen as crate::pyclass::boolean_struct::private::Boolean>::VALUE;\r\n        let u_frozen =\r\n            <<T::BaseType as PyClass>::Frozen as crate::pyclass::boolean_struct::private::Boolean>::VALUE;\r\n        if t_not_frozen && u_frozen {\r\n            // If `T` is a mutable subclass of a frozen `U` base, then it is possible that we need\r\n            // to release the borrow count now. (e.g. `U` may have a noop borrow checker so dropping\r\n            // the `PyRef<U>` later would noop and leak the borrow we currently hold.)\r\n            //\r\n            // However it's nontrivial, if `U` is frozen but itself has a mutable base class `V`,\r\n            // then the borrow checker of both `T` and `U` is the shared borrow checker of `V`.\r\n            //\r\n            // But it's really hard to prove that in the type system, the soundest thing we can do\r\n            // is just add a borrow to `U` now and then release the borrow of `T`.\r\n\r\n            self.as_super()\r\n                .as_class_object()\r\n                .borrow_checker()\r\n                .try_borrow()\r\n                .expect(\"this object is already borrowed\");\r\n\r\n            self.as_class_object().borrow_checker().release_borrow()\r\n        };\r\n        PyClassGuard {\r\n            ptr: core::mem::ManuallyDrop::new(self).ptr,\r\n            marker: PhantomData,\r\n        }\r\n    }\r\n}\r\n\r\nimpl<T: PyClass> Deref for PyClassGuard<'_, T> {\r\n    type Target = T;\r\n\r\n    #[inline]\r\n    fn deref(&self) -> &T {\r\n        // SAFETY: `PyClassObject<T>` contains a valid `T`, by construction no\r\n        // mutable alias is enforced\r\n        unsafe { &*self.as_class_object().get_ptr().cast_const() }\r","sourceCodeStart":258,"sourceCodeEnd":294,"githubUrl":"https://github.com/PyO3/pyo3/blob/ac9b6899d348be4d54614d060dea53a645a12e36/src/pyclass/guard.rs#L258-L294","documentation":"The same 'into_super adds a borrow to the base then releases T' logic in `src/pyclass/guard.rs`: `as_super()` performs `try_borrow` on the base class object and panics with 'this object is already borrowed' if the base is already mutably borrowed. This is the guard (non-Bound) variant of error 85, enforcing that base and derived guards never hold conflicting borrow states.","triggerScenarios":"`PyClassGuard::into_super`/`as_super` called while a mutable guard of the base class exists; a `&mut self` base method calling into subclass code that converts the guard back to the base; dropping guards out of order such that the base remains mutably borrowed.","commonSituations":"`#[pyclass(extends=Base)]` subclasses whose subclass methods invoke base `&mut self` methods and then call `into_super`; wrapper/guard-heavy APIs managing nested class hierarchies.","solutions":["Take the mutable borrow on the base only once and pass it down instead of converting guards repeatedly","Call `into_super` from immutable guards (`PyRef`) only","Check borrow state with `try_borrow` before conversion","Refactor the hierarchy so base mutation happens in a separate, non-overlapping call"],"exampleFix":"// before\nlet base_mut = base.borrow_mut();\nlet guard = sub_guard.into_super(); // panic: base mutably borrowed\n// after\ndrop(base_mut);\nlet guard = sub_guard.into_super();","handlingStrategy":"type-guard","validationCode":"// Rust: verify borrowability before as_super\nlet checker_ok = obj.as_class_object().borrow_checker().try_borrow().is_ok();","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Take base mutability once and pass it down instead of repeated conversions","Keep guard lifetimes short and non-overlapping","Document hierarchy methods that mutate base state","Test extends= hierarchies under re-entrancy"],"tags":["rust","pyo3","inheritance","guard","borrow-checker"],"backgroundTag":"pyclass-super-borrow-conflict","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"}