PyO3/pyo3 · error
Attaching a thread to the interpreter is currently prohibite
Error message
Attaching a thread to the interpreter is currently prohibited.
What it means
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.
Source
Thrown at src/internal/state.rs:339
impl ForbidAttaching {
const FORBIDDEN_DURING_TRAVERSE: &'static str = "Attaching a thread to the interpreter is prohibited while a __traverse__ implementation is running.";
/// Lock access to the interpreter while an implementation of `__traverse__` is running
pub fn during_traverse() -> Self {
Self::new(ATTACH_FORBIDDEN_DURING_TRAVERSE)
}
fn new(reason: isize) -> Self {
let count = ATTACH_COUNT.with(|c| c.replace(reason));
Self { count }
}
#[cold]
fn bail(current: isize) {
match current {
ATTACH_FORBIDDEN_DURING_TRAVERSE => panic!("{}", Self::FORBIDDEN_DURING_TRAVERSE),
_ => panic!("Attaching a thread to the interpreter is currently prohibited."),
}
}
}
impl Drop for ForbidAttaching {
fn drop(&mut self) {
ATTACH_COUNT.with(|c| c.set(self.count));
}
}
/// Registers a Python object pointer inside the release pool, to have its reference count decreased
/// the next time the thread is attached in pyo3.
#[inline]
pub fn register_decref(obj: Py<PyAny>) {
#[cfg(not(pyo3_disable_reference_pool))]
{
POOL.register_decref(obj);
}View on GitHub (pinned to ac9b6899d3)
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
Example fix
// before
impl Drop for MyObj { fn drop(&mut self) { Python::with_gil(|py| log(py)); } } // if dropped inside a guarded section
// after
impl Drop for MyObj { fn drop(&mut self) { DEFER.with(|q| q.borrow_mut().push(log_task())); } } Defensive patterns
Strategy: validation
Validate before calling
// avoid re-entrant attach; check you are not inside a nested PyO3 critical section before calling with_gil
Try / catch
std::panic::catch_unwind(|| pyo3::Python::with_gil(|py| work(py))).unwrap_or_else(|_| schedule_deferred_work());
Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Attaching a thread to the interpreter is prohibited while a
- Cannot attach to the Python interpreter while it is finalizi
- 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/3eff5bd9afd52ec3.
Report an issue: GitHub.