DEV Community

Cover image for Debugging a silent native crash when combining faster-whisper and WinRT on Windows
João Miguel
João Miguel

Posted on

Debugging a silent native crash when combining faster-whisper and WinRT on Windows

I was building a voice assistant: local speech recognition via faster-whisper, reasoning via the Claude API, and local TTS via Windows WinRT. The kind of setup where the only data that leaves your machine is the conversation text.

Everything worked in isolation. But when I put it all together, the process died silently.


The crash

exit code 5
Enter fullscreen mode Exit fullscreen mode

No Python traceback. No error message. Just... gone.

I'd seen exit code 1 before — that's a normal exception. Exit code 5 is different. On Windows, it corresponds to a native access violation (0xC0000005): something in C or C++ tried to read or write memory it doesn't own. Python never gets a chance to catch it because the crash happens below the interpreter, in native code.

The frustrating part: I couldn't reproduce it consistently by commenting things out. Sometimes it crashed. Sometimes it didn't. And I had no output to work with — even my print statements at the top of the file weren't appearing.


The first insight: unbuffered output

When Python crashes with a native exception, stdout may not be flushed. Any print() output that was buffered in Python's I/O layer disappears with the process.

The fix for debugging: run Python with -u (unbuffered):

python -u your_script.py
Enter fullscreen mode Exit fullscreen mode

This forces every print() to go directly to the OS. Now I had output — not much, but enough to see that the last line printed was always inside voz.py, right after the WinRT imports.

The crash was happening at module load time.


Binary search on imports

Module-level crashes in Python are tricky. There's no stack trace, no exception, no line number. The only signal is: which module was loading when the process died?

I narrowed it down by bisection. Comment out half the imports. Run. See which half crashes. Repeat.

After a few rounds, I had isolated the culprit to two specific imports:

import ctranslate2                                      # faster-whisper's backend
from winrt.windows.media.speechsynthesis import SpeechSynthesizer   # WinRT TTS
Enter fullscreen mode Exit fullscreen mode

Each one, imported alone, worked perfectly. Together, in the wrong order, the process crashed every time.


What's actually happening

Both ctranslate2 and WinRT load native DLLs into the Python process at import time. This is standard — Python extensions (.pyd files) are just DLLs.

The problem is what those DLLs do at load time:

WinRT's COM runtime sets up thread-local and process-global state when it initializes. It makes certain assumptions about the state of the Win32 heap and thread apartments.

ctranslate2, when it initializes its CPU backend (or tries to detect GPU capabilities), also does low-level memory operations. If WinRT has already set up certain process-global state that ctranslate2 doesn't expect to find, the access violation follows.

The exact root cause would require reading the ctranslate2 and WinRT DLL source. But the empirical fix is unambiguous:

Load ctranslate2 before WinRT, and the crash never happens.


The fix

In the module where you use WinRT, force ctranslate2 to load its native libraries first — even if you don't use it in that file:

# voz.py

# IMPORTANT: ctranslate2 must be imported before any winrt module.
# Loading WinRT first causes a native crash (exit code 5, 0xC0000005)
# when ctranslate2 later tries to initialize its CPU backend.
try:
    import ctranslate2  # noqa: F401
except ImportError:
    pass  # ctranslate2 not installed — WinRT alone works fine

from winrt.windows.media.speechsynthesis import SpeechSynthesizer
from winrt.windows.storage.streams import DataReader
# ... rest of the module
Enter fullscreen mode Exit fullscreen mode

The try/except ImportError makes this safe: if ctranslate2 isn't installed, the WinRT import proceeds normally.


What didn't work

Before finding this, I tried:

  • Adding explicit import ctranslate2 at the top of the entry point — not enough; the order within each module still matters
  • Wrapping WinRT in a try/except — the crash bypasses Python's exception handling entirely
  • Running in a subprocess — deferred the crash, didn't prevent it
  • Checking for version conflicts — no version combination fixed it; it's a load-order issue, not a version issue

How to know if this is your bug

  • You're using faster-whisper (or anything that imports ctranslate2) and WinRT speech synthesis in the same Python process on Windows
  • The process exits with code 5, or silently with no output
  • The crash disappears when you remove one of the two components
  • Standard Python debugging tools show nothing

The fix: in whichever file imports WinRT first, add import ctranslate2 before the WinRT imports.


The project

This came up while building Gémeo Conselheiro — a voice AI assistant where your laptop does all the heavy lifting: local STT via faster-whisper, local TTS via WinRT (Microsoft Hélia, pt-PT), and only the conversation text goes to the Claude API. There's also a phone call mode that connects your phone over Wi-Fi for hands-free conversation.

If you're building a similar local-first voice AI stack on Windows and hit this, I hope this saves you the time I spent on it.


João Daniel Espanhol Miguel — GitHub · LinkedIn

Top comments (0)