PyO3/pyo3 · error
Attaching a thread to the interpreter is prohibited while a
Error message
Attaching a thread to the interpreter is prohibited while a __traverse__ implementation is running.
What it means
PyO3's AttachGuard::attach() panics with this message when a thread tries to attach to the Python interpreter while a __traverse__ (GC traversal) implementation is executing. PyO3 deliberately forbids attaching new threads during GC traversal because traversing may run arbitrary code that could resurrect objects or re-enter the collector. The ForbidAttaching guard sets an internal counter that makes try_attach fail with AttachError::ForbiddenDuringTraverse.
Source
Thrown at src/internal/state.rs:71
ForbiddenDuringTraverse,
/// The interpreter is not initialized.
NotInitialized,
#[cfg(Py_3_13)]
/// The interpreter is finalizing.
Finalizing,
}
impl AttachGuard {
/// PyO3 internal API for attaching to the Python interpreter. The public API is Python::attach.
///
/// If the thread was already attached via PyO3, this returns
/// `AttachGuard::Assumed`. Otherwise, the thread will attach now and
/// `AttachGuard::Ensured` will be returned.
pub(crate) fn attach() -> Self {
match Self::try_attach() {
Ok(guard) => guard,
Err(AttachError::ForbiddenDuringTraverse) => {
panic!("{}", ForbidAttaching::FORBIDDEN_DURING_TRAVERSE)
}
Err(AttachError::NotInitialized) => {
// try to initialize the interpreter and try again
crate::interpreter_lifecycle::ensure_initialized();
// SAFETY: just initialized the interpreter
unsafe { Self::do_attach_unchecked() }
}
#[cfg(Py_3_13)]
Err(AttachError::Finalizing) => {
panic!("Cannot attach to the Python interpreter while it is finalizing.");
}
}
}
/// Variant of the above which will will return gracefully if the interpreter cannot be attached to.
pub(crate) fn try_attach() -> Result<Self, AttachError> {
match ATTACH_COUNT.try_with(|c| c.get()) {
Ok(i) if i > 0 => {View on GitHub (pinned to ac9b6899d3)
Solutions
- Remove any Python::attach/Python::with_gil calls from inside __traverse__; only touch raw pointers via the traverse visitor
- Move work that needs the GIL out of __traverse__ into drop, __clear__, or ordinary methods
- If you must run Rust code during traversal, keep it GIL-free (operate on borrowed data without calling PyO3 APIs that attach)
- Re-architect the pyclass to hold only raw pointers/simple data so traversal never triggers lazy init
Example fix
// before
unsafe fn __traverse__(&self, visit: PyVisit<'_>) -> Result<(), PyTraverseError> {
let py = Python::with_gil(|py| py.clone()); // panics
visit.call(&self.child)?; Ok(())
}
// after
unsafe fn __traverse__(&self, visit: PyVisit<'_>) -> Result<(), PyTraverseError> {
if let Some(child) = &self.child { visit.call(child)?; } // no GIL attach
Ok(())
} Defensive patterns
Strategy: validation
Validate before calling
if pyo3::internal::state::is_in_gc_traversal() { /* skip attaching work */ } else { /* safe to attach */ } Type guard
fn safe_to_attach() -> bool { !pyo3::internal::state::is_in_gc_traversal() } Try / catch
std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| pyo3::Python::with_gil(|py| work(py)))).unwrap_or_else(|_| { /* defer work */ }) Prevention
- Never call Python::with_gil or Python::attach inside __traverse__
- Keep traverse implementations limited to PyVisit::call on existing references
- Move GIL-requiring cleanup into __clear__ or Drop
When it happens
Trigger: Calling Python::attach / Python::with_gil (or any API that internally attaches the thread) from within a __traverse__ implementation, e.g. inside unsafe fn __traverse__ of a #[pyclass] with gc protocol, or in a callback invoked during gc.collect().
Common situations: Implementing PyGCProtocol traverse for a pyclass holding references that trigger lazy initialization (which internally attaches the GIL); calling Python::with_gil inside a traverse visitor; dropping/lazily-initializing extension objects mid-traversal that re-enter PyO3 APIs.
Related errors
- Cannot attach to the Python interpreter while it is finalizi
- Attaching a thread to the interpreter is currently prohibite
- Cannot clone pointer into Python heap without the thread bei
- Cannot drop pointer into Python heap without the thread bein
- {}
AI-assisted analysis of PyO3/pyo3@ac9b6899d3 (2026-09-05).
Data as JSON: /api/errors/d9c151416dd6c35c.
Report an issue: GitHub.