PyO3/pyo3 · error

Already borrowed

Error message

Already borrowed

What it means

Symmetric to the mutable-borrow panic: `PyRefMut::borrow` panics with 'Already borrowed' when requesting a mutable borrow while the object is already shared-borrowed (one or more `PyRef`s alive). PyO3 enforces at runtime the same rules the Rust compiler enforces at compile time for `&`/`&mut`, since Python re-entrancy can create aliases the compiler cannot see.

Source

Thrown at src/pycell.rs:588

    pub fn as_ptr(&self) -> *mut ffi::PyObject {
        self.inner.as_ptr()
    }

    /// Returns an owned raw FFI pointer represented by self.
    ///
    /// # Safety
    ///
    /// The reference is owned; when finished the caller should either transfer ownership
    /// of the pointer or decrease the reference count (e.g. with [`pyo3::ffi::Py_DecRef`](crate::ffi::Py_DecRef)).
    #[inline]
    pub fn into_ptr(self) -> *mut ffi::PyObject {
        self.inner.clone().into_ptr()
    }

    #[inline]
    #[track_caller]
    pub(crate) fn borrow(obj: &Bound<'py, T>) -> Self {
        Self::try_borrow(obj).expect("Already borrowed")
    }

    pub(crate) fn try_borrow(obj: &Bound<'py, T>) -> Result<Self, PyBorrowMutError> {
        let cell = obj.get_class_object();
        cell.ensure_threadsafe();
        cell.borrow_checker()
            .try_borrow_mut()
            .map(|_| Self { inner: obj.clone() })
    }

    pub(crate) fn downgrade(slf: &Self) -> &PyRef<'py, T> {
        let ptr = NonNull::from(slf).cast();
        // SAFETY: `PyRefMut<T>` and `PyRef<T>` have the same layout
        unsafe { ptr.as_ref() }
    }
}

impl<'p, T> PyRefMut<'p, T>

View on GitHub (pinned to ac9b6899d3)

Solutions

  1. Drop the `PyRef` before calling the `&mut self` method
  2. Use `try_borrow_mut` and handle the `PyBorrowMutError` gracefully
  3. Clone needed data out of the immutable borrow first, then take the mutable one
  4. Use `py.allow_threads` or interior mutability (`RefCell`/locks) to avoid overlapping borrows

Example fix

// before
let r = obj.borrow(py);
r.method_mut(py); // still shared-borrowed -> panic
// after
drop(obj.borrow(py));
obj.borrow_mut(py).method_mut();
Defensive patterns

Strategy: try-catch

Validate before calling

// Rust
let _ = obj.try_borrow_mut(py).map_err(PyErr::from)?;

Try / catch

// Rust
match obj.try_borrow_mut(py) {
    Ok(mut m) => { /* work */ },
    Err(_) => return Err(PyBorrowMutError::new().into()),
}

Prevention

When it happens

Trigger: Two Python references to the same `#[pyclass]` object with both `&self` and `&mut self` methods active simultaneously, e.g. calling a `&self` method whose result callback enters a `&mut self` method on the same instance; holding a `PyRef` while calling `pyo3::py_run!`/attribute assignment that needs `PyRefMut`.

Common situations: Python-level property setters (`&mut self`) invoked while a getter's `PyRef` is still held; nested iteration over the same object; callbacks fired from within a shared-borrow scope.

Related errors


AI-assisted analysis of PyO3/pyo3@ac9b6899d3 (2026-09-05). Data as JSON: /api/errors/1f7a8d12c7004279. Report an issue: GitHub.