{"record":{"id":"d9c151416dd6c35c","repo":"PyO3/pyo3","slug":"attaching-a-thread-to-the-interpreter-is-prohibite","errorCode":null,"errorMessage":"Attaching a thread to the interpreter is prohibited while a __traverse__ implementation is running.","messagePattern":"Attaching a thread to the interpreter is prohibited while a __traverse__ implementation is running\\.","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/internal/state.rs","lineNumber":71,"sourceCode":"    ForbiddenDuringTraverse,\n    /// The interpreter is not initialized.\n    NotInitialized,\n    #[cfg(Py_3_13)]\n    /// The interpreter is finalizing.\n    Finalizing,\n}\n\nimpl AttachGuard {\n    /// PyO3 internal API for attaching to the Python interpreter. The public API is Python::attach.\n    ///\n    /// If the thread was already attached via PyO3, this returns\n    /// `AttachGuard::Assumed`. Otherwise, the thread will attach now and\n    /// `AttachGuard::Ensured` will be returned.\n    pub(crate) fn attach() -> Self {\n        match Self::try_attach() {\n            Ok(guard) => guard,\n            Err(AttachError::ForbiddenDuringTraverse) => {\n                panic!(\"{}\", ForbidAttaching::FORBIDDEN_DURING_TRAVERSE)\n            }\n            Err(AttachError::NotInitialized) => {\n                // try to initialize the interpreter and try again\n                crate::interpreter_lifecycle::ensure_initialized();\n                // SAFETY: just initialized the interpreter\n                unsafe { Self::do_attach_unchecked() }\n            }\n            #[cfg(Py_3_13)]\n            Err(AttachError::Finalizing) => {\n                panic!(\"Cannot attach to the Python interpreter while it is finalizing.\");\n            }\n        }\n    }\n\n    /// Variant of the above which will will return gracefully if the interpreter cannot be attached to.\n    pub(crate) fn try_attach() -> Result<Self, AttachError> {\n        match ATTACH_COUNT.try_with(|c| c.get()) {\n            Ok(i) if i > 0 => {","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/PyO3/pyo3/blob/ac9b6899d348be4d54614d060dea53a645a12e36/src/internal/state.rs#L53-L89","documentation":"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.","triggerScenarios":"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().","commonSituations":"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.","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"],"exampleFix":"// before\nunsafe fn __traverse__(&self, visit: PyVisit<'_>) -> Result<(), PyTraverseError> {\n    let py = Python::with_gil(|py| py.clone()); // panics\n    visit.call(&self.child)?; Ok(())\n}\n// after\nunsafe fn __traverse__(&self, visit: PyVisit<'_>) -> Result<(), PyTraverseError> {\n    if let Some(child) = &self.child { visit.call(child)?; } // no GIL attach\n    Ok(())\n}","handlingStrategy":"validation","validationCode":"if pyo3::internal::state::is_in_gc_traversal() { /* skip attaching work */ } else { /* safe to attach */ }","typeGuard":"fn safe_to_attach() -> bool { !pyo3::internal::state::is_in_gc_traversal() }","tryCatchPattern":"std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| pyo3::Python::with_gil(|py| work(py)))).unwrap_or_else(|_| { /* defer work */ })","preventionTips":["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"],"tags":["pyo3","gil","gc-traverse","panic"],"backgroundTag":"gil-attach-forbidden-during-traverse","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"}