[Bug]: 3.10 debug build crashes on interpreter exit · Issue #32509 · matplotlib/matplotlib · GitHub
Skip to content

[Bug]: 3.10 debug build crashes on interpreter exit #32509

Open

Description

@bartoli

Bug summary

I'm building a full debug Python environment (Python 3.12.4, numpy 2.4.6, pybind 3.1, contourpy 1.3.3, MPL 3.10.9)

It seems that when MPL has been imported, the python interpreter shutdown will show a GIL error:

Fatal Python error: _PyInterpreterState_GET: the function must be called with the GIL held, after Python initialization and before Python finalization, but the GIL is released (the current Python thread state is NULL)
Python runtime state: finalizing (tstate=0x00007ffbe152fca0)

In release builds, this does not occur, but I suppose it's because there are less asserts on GIL state at shutdown.

When debugging, the stack is

 	KernelBase.dll!00007ff843eb3122()	Unknown
 	python312_d.dll!fatal_error_exit(int status) Line 2778	C
 	python312_d.dll!fatal_error(int fd, int header, const char * prefix, const char * msg, int status) Line 2959	C
 	python312_d.dll!_Py_FatalErrorFunc(const char * func, const char * msg) Line 2975	C
 	python312_d.dll!_PyInterpreterState_GET() Line 131	C
 	python312_d.dll!_Py_DECREF_DecRefTotal() Line 240	C
 	ft2font_d.cp312-win_amd64.pyd!Py_DECREF(const char * filename, int lineno, _object * op) Line 688	C++
 	ft2font_d.cp312-win_amd64.pyd!Py_XDECREF(_object * op) Line 798	C++
 	ft2font_d.cp312-win_amd64.pyd!pybind11::handle::dec_ref() Line 300	C++
 	ft2font_d.cp312-win_amd64.pyd!pybind11::object::~object() Line 395	C++
 	ft2font_d.cp312-win_amd64.pyd!std::pair<std::string const ,pybind11::object>::~pair<std::string const ,pybind11::object>()	C++
 	ft2font_d.cp312-win_amd64.pyd!std::pair<std::string const ,pybind11::object>::`scalar deleting destructor'(unsigned int)	C++
 	ft2font_d.cp312-win_amd64.pyd!std::_Default_allocator_traits<std::allocator<std::_List_node<std::pair<std::string const ,pybind11::object>,void *>>>::destroy<std::pair<std::string const ,pybind11::object>>(std::allocator<std::_List_node<std::pair<std::string const ,pybind11::object>,void *>> & __formal, std::pair<std::string const ,pybind11::object> * const _Ptr) Line 741	C++
 	ft2font_d.cp312-win_amd64.pyd!std::_List_node<std::pair<std::string const ,pybind11::object>,void *>::_Freenode<std::allocator<std::_List_node<std::pair<std::string const ,pybind11::object>,void *>>>(std::allocator<std::_List_node<std::pair<std::string const ,pybind11::object>,void *>> & _Al, std::_List_node<std::pair<std::string const ,pybind11::object>,void *> * _Ptr) Line 318	C++
 	ft2font_d.cp312-win_amd64.pyd!std::_List_node<std::pair<std::string const ,pybind11::object>,void *>::_Free_non_head<std::allocator<std::_List_node<std::pair<std::string const ,pybind11::object>,void *>>>(std::allocator<std::_List_node<std::pair<std::string const ,pybind11::object>,void *>> & _Al, std::_List_node<std::pair<std::string const ,pybind11::object>,void *> * _Head) Line 329	C++
 	ft2font_d.cp312-win_amd64.pyd!std::list<std::pair<std::string const ,pybind11::object>,std::allocator<std::pair<std::string const ,pybind11::object>>>::_Tidy() Line 1519	C++
 	ft2font_d.cp312-win_amd64.pyd!std::list<std::pair<std::string const ,pybind11::object>,std::allocator<std::pair<std::string const ,pybind11::object>>>::~list<std::pair<std::string const ,pybind11::object>,std::allocator<std::pair<std::string const ,pybind11::object>>>() Line 1063	C++
 	ft2font_d.cp312-win_amd64.pyd!std::_Hash<std::_Umap_traits<std::string,pybind11::object,std::_Uhash_compare<std::string,std::hash<std::string>,std::equal_to<std::string>>,std::allocator<std::pair<std::string const ,pybind11::object>>,0>>::~_Hash<std::_Umap_traits<std::string,pybind11::object,std::_Uhash_compare<std::string,std::hash<std::string>,std::equal_to<std::string>>,std::allocator<std::pair<std::string const ,pybind11::object>>,0>>()	C++
 	ft2font_d.cp312-win_amd64.pyd!std::unordered_map<std::string,pybind11::object,std::hash<std::string>,std::equal_to<std::string>,std::allocator<std::pair<std::string const ,pybind11::object>>>::~unordered_map<std::string,pybind11::object,std::hash<std::string>,std::equal_to<std::string>,std::allocator<std::pair<std::string const ,pybind11::object>>>()	C++
 	ft2font_d.cp312-win_amd64.pyd!p11x::`anonymous namespace'::`dynamic atexit destructor for 'enums''()	C++
 	ucrtbased.dll!00007fff08e62b99()	Unknown
 	ucrtbased.dll!00007fff08e625c5()	Unknown
 	ucrtbased.dll!00007fff08e62717()	Unknown
 	ucrtbased.dll!00007fff08e62d44()	Unknown
 	ft2font_d.cp312-win_amd64.pyd!__scrt_dllmain_uninitialize_c() Line 398	C++
>	ft2font_d.cp312-win_amd64.pyd!dllmain_crt_process_detach(const bool is_terminating) Line 182	C++
 	ft2font_d.cp312-win_amd64.pyd!dllmain_crt_dispatch(HINSTANCE__ * const instance, const unsigned long reason, void * const reserved) Line 220	C++
 	ft2font_d.cp312-win_amd64.pyd!dllmain_dispatch(HINSTANCE__ * const instance, const unsigned long reason, void * const reserved) Line 293	C++
 	ft2font_d.cp312-win_amd64.pyd!_DllMainCRTStartup(HINSTANCE__ * const instance, const unsigned long reason, void * const reserved) Line 335	C++
 	ntdll.dll!00007ff84667ef6a()	Unknown
 	ntdll.dll!00007ff84657b2e3()	Unknown
 	ntdll.dll!00007ff8465e027f()	Unknown
 	ntdll.dll!00007ff8465def8e()	Unknown
 	kernel32.dll!00007ff8447a17ab()	Unknown
 	ucrtbased.dll!00007fff08e6219b()	Unknown
 	ucrtbased.dll!00007fff08e6214f()	Unknown
 	ucrtbased.dll!00007fff08e62456()	Unknown
 	python_d.exe!__scrt_common_main_seh() Line 295	C++
 	python_d.exe!__scrt_common_main() Line 331	C++
 	python_d.exe!wmainCRTStartup(void * __formal) Line 17	C++
 	kernel32.dll!00007ff84478dda7()	Unknown
 	ntdll.dll!00007ff8465dee4c()	Unknown

It seems the enums map is deleted in scalar destructor at exit, far too late for python, and the decref crashes because of it.

As a workaround, since the object is not properly deleted anyways,
you can replace the line of _enums.h:
auto enums = std::unordered_map<std::string, py::object>{};
by
auto& enums = *new std::unordered_map<std::string, py::object>{};
So no delete attempt is done, and the problem disappears

Code changed in 3.11, so i suppose this only affects 3.10.x

Code for reproduction

python_d -c "import matplotlib"

Actual outcome

Fatal Python error: _PyInterpreterState_GET: the function must be called with the GIL held, after Python initialization and before Python finalization, but the GIL is released (the current Python thread state is NULL)
Python runtime state: finalizing (tstate=0x00007ffbe152fca0)

Expected outcome

Process imports matplotlib and finished without a GIL error

Additional information

No response

Operating system

WINdows 11

Matplotlib Version

3.10.9

Matplotlib Backend

backend independant

Python version

3.12.4 debug build

Jupyter version

No response

Installation

from source (.tar.gz)

Activity

  1. iccir commented on Oct 20, 2026

    @iccir
    Contributor

    Realistically, I don't think that this will be fixed for 3.10 unless it is a security issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions