PyO3/pyo3 · critical
Cannot clone pointer into Python heap without the thread…
Error message
Cannot clone pointer into Python heap without the thread being attached.
What it means
Panic from Clone for a pointer type wrapping a Python heap object. Cloning bumps the CPython reference count via Py_INCREF, which is only legal while the calling thread is attached to the Python interpreter (holding the GIL or, on free-threaded builds, being attached). If the thread is not attached, incrementing the refcount would be unsynchronized/invalid, so the code aborts instead of corrupting the refcount.
Solutions
- Wrap the clone in Python::with_gil(|py| ...) (or Python::attach) so the thread is attached first
- Don't share Py<T> across unattached threads; send via channels and clone inside with_gil
- Avoid touching Python objects during interpreter shutdown (use before-thread/atexit hooks to drop them earlier)
Example fix
// before (foreign thread) let cloned = py_obj.clone(); // panics: not attached // after let cloned = Python::with_gil(|_py| py_obj.clone());
Defensive patterns
Strategy: type-guard
Validate before calling
fn can_touch_python() -> bool { Python::with_gil(|_| true) } // only call on threads you attach yourself Type guard
fn is_attached() -> bool { Python::with_gil(|_py| /* reached means attached */ true) } Try / catch
// guard foreign-thread access std::panic::catch_unwind(|| Python::with_gil(|_py| py_obj.clone())).is_err()
Prevention
- Clone Py<T> only inside Python::with_gil / Python::attach
- Never touch Python objects on unattached OS threads
- Drop Py<T> values before interpreter finalization (atexit/before-thread hooks)
When it happens
Trigger: Calling .clone() on a Py<T>/Borrowed pointer from a thread that is not attached to Python (e.g. outside Python::with_gil, on a foreign thread without Python::attach/with_gil), or during interpreter finalization when attachment is refused.
Common situations: Rust threads spawned manually touching Py<T> clones without Python::with_gil; callbacks from non-Python threads; cloning during interpreter shutdown.
Related errors
- Attaching a thread to the interpreter is currently…
- Attaching a thread to the interpreter is prohibited while a…
- Cannot attach to the Python interpreter while it is…
- 0 < N <= 12
- attempted to fetch exception but none was set
AI-assisted analysis of PyO3/pyo3@ac9b6899d3 (2026-09-05).
Data as JSON: /api/errors/14f89b70482aa895.
Report an issue: GitHub.
Appendix: source
Thrown at src/instance.rs:2266
#[inline]
fn clone(&self) -> Self {
#[track_caller]
#[inline]
fn try_incref(obj: NonNull<ffi::PyObject>) {
use crate::internal::state::thread_is_attached;
if thread_is_attached() {
// SAFETY: Py_INCREF is safe to call on a valid Python object if the thread is attached.
unsafe { ffi::Py_INCREF(obj.as_ptr()) }
} else {
incref_failed()
}
}
#[cold]
#[track_caller]
fn incref_failed() -> ! {
panic!("Cannot clone pointer into Python heap without the thread being attached.");
}
try_incref(self.0);
Self(self.0, PhantomData)
}
}
/// Dropping a `Py` instance decrements the reference count
/// on the object by one if the thread is attached to the Python interpreter.
///
/// Otherwise and by default, this registers the underlying pointer to have its reference count
/// decremented the next time PyO3 attaches to the Python interpreter.
///
/// However, if the `pyo3_disable_reference_pool` conditional compilation flag
/// is enabled, it will abort the process.
impl<T> Drop for Py<T> {
#[inline]View on GitHub (pinned to ac9b6899d3)