{"record":{"id":"25278c09b0647c19","repo":"PyO3/pyo3","slug":"cannot-drop-pointer-into-python-heap-without-the-t","errorCode":null,"errorMessage":"Cannot drop pointer into Python heap without the thread being attached.","messagePattern":"Cannot drop pointer into Python heap without the thread being attached\\.","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"src/internal/state.rs","lineNumber":364,"sourceCode":"        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    }\n    #[cfg(all(\n        pyo3_disable_reference_pool,\n        not(pyo3_leak_on_drop_without_reference_pool)\n    ))]\n    {\n        let _trap = PanicTrap::new(\"Aborting the process to avoid panic-from-drop.\");\n        panic!(\"Cannot drop pointer into Python heap without the thread being attached.\");\n    }\n}\n\n/// Private helper function to check if we are currently in a GC traversal (as detected by PyO3).\n#[cfg(any(not(Py_LIMITED_API), Py_3_11))]\npub(crate) fn is_in_gc_traversal() -> bool {\n    ATTACH_COUNT\n        .try_with(|c| c.get() == ATTACH_FORBIDDEN_DURING_TRAVERSE)\n        .unwrap_or(false)\n}\n\n/// Increments pyo3's internal attach count - to be called whenever an AttachGuard is created.\n#[inline(always)]\nfn increment_attach_count() {\n    // Ignores the error in case this function called from `atexit`.\n    let _ = ATTACH_COUNT.try_with(|c| {\n        let current = c.get();\n        if current < 0 {","sourceCodeStart":346,"sourceCodeEnd":382,"githubUrl":"https://github.com/PyO3/pyo3/blob/ac9b6899d348be4d54614d060dea53a645a12e36/src/internal/state.rs#L346-L382","documentation":"register_decref panics with this message and then aborts the process (PanicTrap) when reference-count dropping is attempted without a thread attached, under the pyo3_disable_reference_pool configuration (with pyo3_leak_on_drop_without_reference_pool off). PyO3 cannot safely decref a Python object from an unattached thread, so it deliberately aborts rather than corrupt refcounts.","triggerScenarios":"Dropping a PyObject/GIL-independent wrapper (e.g. Py<T>) on a thread that never called Python::with_gil, when compiled with pyo3_disable_reference_pool and not pyo3_leak_on_drop_without_reference_pool.","commonSituations":"Sending Py<T> handles across threads and dropping them in workers; dropping cached objects at thread teardown; custom builds that disabled the reference pool for performance.","solutions":["Re-enable PyO3's reference pool (remove pyo3_disable_reference_pool from cargo features)","Enable pyo3_leak_on_drop_without_reference_pool if leaking instead of aborting is acceptable","Wrap drops in Python::with_gil(|py| { drop(obj) }) or Python::detach boundaries so decref happens on an attached thread","Avoid moving Py<T> values to threads that drop them unattached; send owned handles back to a GIL-holding thread"],"exampleFix":"// before\nlet obj: Py<MyType> = ...;\nstd::thread::spawn(move || drop(obj)); // unattached drop: aborts\n// after\nlet obj: Py<MyType> = ...;\nstd::thread::spawn(move || Python::with_gil(|_py| drop(obj)));","handlingStrategy":"validation","validationCode":"// before dropping a Py<T> on this thread, ensure attachment\nfn drop_attached<T: pyo3::IntoPyObjectExt + 'static>(obj: pyo3::Py<T>) {\n    pyo3::Python::with_gil(|_py| drop(obj));\n}","typeGuard":"fn can_drop_py_value() -> bool { pyo3::Python::try_attach().is_some() || cfg!(not(feature = \"pyo3_disable_reference_pool\")) }","tryCatchPattern":null,"preventionTips":["Only drop Py<T> values while the thread is attached (inside with_gil) or via the reference pool","Do not compile with pyo3_disable_reference_pool unless all unattached drops are avoided or leaks are accepted via pyo3_leak_on_drop_without_reference_pool","Keep Python object ownership on the GIL-holding thread"],"tags":["pyo3","refcount","gil","process-abort"],"backgroundTag":"drop-without-gil-attached","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"}