An embarrassing oopsnik about forgetting edge cases.
I was using per-thread records to store exception data, error messages & backtraces, self-profiling counters.
Instead of threadvar it was organized as a linked list so every time pointer to it was asked, the chain was searched for one with the right thread id.
Last Sunday I finally redid it using threadvar.
I did not change the API, the function is still called and still allocates the record the first time it is asked for. It just uses threadvar to store and retrieve it. But the linked list is still used, reserved for exactly two cases: the module's thread manager collating error messages from all threads at shutdown and the histogram renderer, which iterates over all known threads.
Looking at histograms made me realize the blunder causing uncaught exceptions on exit.

See that mysterious unnamed thread and the main thread's bar being empty?
That's where I forgot that the record for the main thread should not be allocated via New, the pointer must point to a specific global variable which is the start of the linked list.

I also had enough with the horribly convoluted and distributed state machine of the control panel (which I am still trying to wrangle into a separate built-inmodule) playing tricks and taking me on wild goose chases.
I nuked it and am now re-making it from scratch. As I should have from the start.

Also, I decided to make the control of the verbose log (aka verbal diarrhea mode) granular instead of "on" and "off". Finding points of interest in kilometer-long walls of text was becoming tiresome. Soon I will be able to switch verbose logging separately for gapi, database, framework, path processing, loading external function pointers and so on.
The debugging executable version would be left for cases of extreme debugging.