{"record":{"id":"3eff5bd9afd52ec3","repo":"PyO3/pyo3","slug":"attaching-a-thread-to-the-interpreter-is-currently","errorCode":null,"errorMessage":"Attaching a thread to the interpreter is currently prohibited.","messagePattern":"Attaching a thread to the interpreter is currently prohibited\\.","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/internal/state.rs","lineNumber":339,"sourceCode":"impl ForbidAttaching {\n    const FORBIDDEN_DURING_TRAVERSE: &'static str = \"Attaching a thread to the interpreter is prohibited while a __traverse__ implementation is running.\";\n\n    /// Lock access to the interpreter while an implementation of `__traverse__` is running\n    pub fn during_traverse() -> Self {\n        Self::new(ATTACH_FORBIDDEN_DURING_TRAVERSE)\n    }\n\n    fn new(reason: isize) -> Self {\n        let count = ATTACH_COUNT.with(|c| c.replace(reason));\n\n        Self { count }\n    }\n\n    #[cold]\n    fn bail(current: isize) {\n        match current {\n            ATTACH_FORBIDDEN_DURING_TRAVERSE => panic!(\"{}\", Self::FORBIDDEN_DURING_TRAVERSE),\n            _ => panic!(\"Attaching a thread to the interpreter is currently prohibited.\"),\n        }\n    }\n}\n\nimpl Drop for ForbidAttaching {\n    fn drop(&mut self) {\n        ATTACH_COUNT.with(|c| c.set(self.count));\n    }\n}\n\n/// Registers a Python object pointer inside the release pool, to have its reference count decreased\n/// the next time the thread is attached in pyo3.\n#[inline]\npub fn register_decref(obj: Py<PyAny>) {\n    #[cfg(not(pyo3_disable_reference_pool))]\n    {\n        POOL.register_decref(obj);\n    }","sourceCodeStart":321,"sourceCodeEnd":357,"githubUrl":"https://github.com/PyO3/pyo3/blob/ac9b6899d348be4d54614d060dea53a645a12e36/src/internal/state.rs#L321-L357","documentation":"ForbidAttaching::bail's fallback arm panics when attaching is currently prohibited for a reason other than GC traversal (counter is non-zero but not the traversal sentinel). It indicates a nested/overlapping ForbidAttaching guard or a re-entrancy bug in PyO3-internal critical sections.","triggerScenarios":"Attempting to attach while any ForbidAttaching guard is held outside __traverse__ — typically caused by re-entrant calls into APIs that install such a guard, or calling attach from code invoked by another attach-forbidding critical section.","commonSituations":"Deep re-entrancy: a callback/drop handler invoked inside a protected section calls back into PyO3; misuse of internal gc-safety utilities; bugs in custom wrappers around PyO3 internals.","solutions":["Identify the enclosing critical section and remove the nested attach call","Avoid re-entrant PyO3 API calls from within code that holds internal guards (drops, callbacks, destructors)","Queue the work and execute it after the outer operation completes","Update PyO3 — re-entrancy panics may be fixed in newer releases"],"exampleFix":"// before\nimpl Drop for MyObj { fn drop(&mut self) { Python::with_gil(|py| log(py)); } } // if dropped inside a guarded section\n// after\nimpl Drop for MyObj { fn drop(&mut self) { DEFER.with(|q| q.borrow_mut().push(log_task())); } }","handlingStrategy":"validation","validationCode":"// avoid re-entrant attach; check you are not inside a nested PyO3 critical section before calling with_gil","typeGuard":null,"tryCatchPattern":"std::panic::catch_unwind(|| pyo3::Python::with_gil(|py| work(py))).unwrap_or_else(|_| schedule_deferred_work());","preventionTips":["Avoid calling PyO3 APIs from Drop/callbacks that may run inside guarded sections","Queue side effects and run them outside the critical section","Keep internal critical sections free of re-entrant attach calls"],"tags":["pyo3","gil","reentrancy","panic"],"backgroundTag":"gil-attach-prohibited","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"}