PyO3/pyo3 · error
Cannot attach to the Python interpreter while it is finalizi
Error message
Cannot attach to the Python interpreter while it is finalizing.
What it means
This panic fires from Python::attach()/AttachGuard::attach when the interpreter has begun finalizing (Python 3.13+, via AttachError::Finalizing). Once CPython is shutting down, attaching a thread to obtain the GIL is unsafe and undefined, so PyO3 refuses with a panic instead of blocking forever or crashing in C code.
Source
Thrown at src/internal/state.rs:81
///
/// 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 => {
// SAFETY: We just checked that the thread is already attached.
return Ok(unsafe { Self::assume() });
}
// Cannot attach during GC traversal.
Ok(ATTACH_FORBIDDEN_DURING_TRAVERSE) => {
return Err(AttachError::ForbiddenDuringTraverse)
}
// other cases handled below
_ => {}
}View on GitHub (pinned to ac9b6899d3)
Solutions
- Ensure worker threads are joined/stopped before Py_Finalize or process exit
- Use Python::with_embedded_python_interpreter/teardown utilities that shut threads down first
- Check the interpreter isn't finalizing with Python::version/pool guards — use try_attach-based APIs (Python::try_attach) and skip work on error
- In 3.13+ embeddings, call Py_Finalize only after all PyO3-using threads have terminated
Example fix
// before
std::thread::spawn(|| Python::with_gil(|py| run(py))); // may run during finalize
// after
let handle = std::thread::spawn(|| Python::with_gil(|py| run(py)));
handle.join().expect("worker");
// then finalize the interpreter Defensive patterns
Strategy: validation
Validate before calling
// PyO3 0.23+: check via pool/attach result before work
if pyo3::Python::try_attach().is_none() { return; } // skip if not attachable Type guard
fn interpreter_usable() -> bool { pyo3::Python::try_attach().is_some() } Try / catch
std::panic::catch_unwind(|| pyo3::Python::with_gil(|py| work(py))).unwrap_or_default();
Prevention
- Join all Python-touching threads before Py_Finalize / process exit
- Stop background workers (rayon/tokio) during interpreter teardown
- Avoid Python calls in Drop impls that may run at shutdown
When it happens
Trigger: Calling Python::with_gil / Python::attach during interpreter shutdown: destructors/drop handlers running after Py_Finalize, daemon threads still executing Python calls, atexit or __del__ code that re-enters PyO3 late in teardown.
Common situations: Background worker threads (rayon/tokio/std threads) still alive when the embedding application or interpreter calls Py_Finalize; objects dropped in process exit paths; long-lived caches flushed after finalization started.
Related errors
- Attaching a thread to the interpreter is prohibited while a
- 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/6603801bcbd91e62.
Report an issue: GitHub.