Summary
A PyQt6 desktop application crashes with a Windows access violation (STATUS_ACCESS_VIOLATION, 0xC0000005) when the main window is closed, but only in sessions where a background QThreadPool/QRunnable worker had loaded and displayed data (image arrays via numpy/tifffile/zarr, rendered with pyqtgraph) earlier in that session. Sessions where nothing was ever loaded close cleanly every time.
The crash produces no Python-level traceback or exception (it bypasses sys.excepthook/threading.excepthook entirely, as expected for a native access violation) — it was only diagnosed via the Windows Application event log's own crash record, which points squarely at PyQt6-sip itself rather than application code.
Upgrading PyQt6-sip from 13.11.1 to 13.12.0 (no other change) eliminated the crash across a subsequent real session that reproduced the same close-after-load pattern that crashed every time before the upgrade.
Environment
- OS: Windows 11 Pro 10.0.26200
- Python: 3.14.5 (
tags/v3.14.5:5607950, May 10 2026, 10:43:50) [MSC v.1944 64 bit (AMD64)]
- PyQt6: 6.11.0
- PyQt6-Qt6: 6.11.1
- PyQt6-sip: 13.11.1 (crashes) → 13.12.0 (does not crash)
Crash signature (from Windows Event Log, Application log, Event ID 1000/1001)
Identical across three independent crash occurrences over two separate sessions:
Faulting application name: python.exe, version: 3.14.5150.1013, time stamp: 0x6a0062cc
Faulting module name: sip.cp314-win_amd64.pyd, version: 0.0.0.0, time stamp: 0x69aebad1
Exception code: 0xc0000005
Fault offset: 0x00000000000107b6
Faulting module path: <venv>\Lib\site-packages\PyQt6\sip.cp314-win_amd64.pyd
Hashed bucket (WER fault bucket): d60846680b1f6807a059806d29041443
Same faulting module, same time stamp, same exact byte offset, and the same WER hashed bucket every time — this reads as a deterministic bug in a specific object-teardown code path, not a timing-dependent race.
Circumstances that reproduce it
- A
QMainWindow-based app with QApplication.setQuitOnLastWindowClosed(True).
- A
QThreadPool (QThreadPool(self), a handful of max threads) running QRunnable workers that read files from disk (numpy/tifffile/zarr arrays) and emit results back to the main thread via Qt signals.
- Those results get displayed via
pyqtgraph (image items, plot curves, ROI overlays).
- The user closes the main window normally (clicking the close button / calling
.close()), triggering closeEvent(), which does some settings/state saving and then calls super().closeEvent(event).
- The process then exits via
sys.exit(app.exec()) returning.
Crash happens at or shortly after step 4/5. It never happened in a session where step 2–3 never ran (i.e., the window was closed with nothing ever loaded).
What I was not able to produce
An isolated, dependency-free minimal repro outside the full application. I built one using QT_QPA_PLATFORM=offscreen (same window/QThreadPool/close pattern, synthetic TIFF data, no real display) and could not reproduce the crash there — it hung instead of crashing, which suggests the offscreen platform plugin skips whatever native paint/window-teardown path the real (on-screen) crash goes through. I don't have a minimized script to attach; happy to help narrow this further if it's useful (e.g. testing a debug/symbol build, or a smaller repro under a real display rather than offscreen).
Possibly related (unconfirmed)
pytest-dev/pytest#14500 reports "Windows fatal exception: access violation (GC crash on exit)" also on Python 3.14.5, in an unrelated project. I can't confirm it's the same root cause, but it suggests Python 3.14's shutdown/GC behavior may be exposing latent bugs in multiple C-extension modules that haven't yet fully adapted, not something unique to sip. Flagging in case it's a useful cross-reference.
Expected vs actual
- Expected: closing the main window and exiting the process cleanly (exit code 0), same as any session where no dataset was loaded.
- Actual: exit code 3221225477 /
0xC0000005, no Python exception, in sip.cp314-win_amd64.pyd, only in sessions with prior background-thread-loaded data.
Fix confirmed
Upgrading PyQt6-sip 13.11.1 → 13.12.0 (pip install --upgrade PyQt6-sip==13.12.0), with no other code or environment changes, resolved it — verified by reproducing the exact "load a dataset, then close" sequence that crashed reliably before the upgrade.
Summary
A PyQt6 desktop application crashes with a Windows access violation (
STATUS_ACCESS_VIOLATION,0xC0000005) when the main window is closed, but only in sessions where a backgroundQThreadPool/QRunnableworker had loaded and displayed data (image arrays via numpy/tifffile/zarr, rendered with pyqtgraph) earlier in that session. Sessions where nothing was ever loaded close cleanly every time.The crash produces no Python-level traceback or exception (it bypasses
sys.excepthook/threading.excepthookentirely, as expected for a native access violation) — it was only diagnosed via the Windows Application event log's own crash record, which points squarely atPyQt6-sipitself rather than application code.Upgrading
PyQt6-sipfrom 13.11.1 to 13.12.0 (no other change) eliminated the crash across a subsequent real session that reproduced the same close-after-load pattern that crashed every time before the upgrade.Environment
tags/v3.14.5:5607950, May 10 2026, 10:43:50)[MSC v.1944 64 bit (AMD64)]Crash signature (from Windows Event Log,
Applicationlog, Event ID 1000/1001)Identical across three independent crash occurrences over two separate sessions:
Same faulting module, same time stamp, same exact byte offset, and the same WER hashed bucket every time — this reads as a deterministic bug in a specific object-teardown code path, not a timing-dependent race.
Circumstances that reproduce it
QMainWindow-based app withQApplication.setQuitOnLastWindowClosed(True).QThreadPool(QThreadPool(self), a handful of max threads) runningQRunnableworkers that read files from disk (numpy/tifffile/zarr arrays) and emit results back to the main thread via Qt signals.pyqtgraph(image items, plot curves, ROI overlays)..close()), triggeringcloseEvent(), which does some settings/state saving and then callssuper().closeEvent(event).sys.exit(app.exec())returning.Crash happens at or shortly after step 4/5. It never happened in a session where step 2–3 never ran (i.e., the window was closed with nothing ever loaded).
What I was not able to produce
An isolated, dependency-free minimal repro outside the full application. I built one using
QT_QPA_PLATFORM=offscreen(same window/QThreadPool/close pattern, synthetic TIFF data, no real display) and could not reproduce the crash there — it hung instead of crashing, which suggests the offscreen platform plugin skips whatever native paint/window-teardown path the real (on-screen) crash goes through. I don't have a minimized script to attach; happy to help narrow this further if it's useful (e.g. testing a debug/symbol build, or a smaller repro under a real display rather than offscreen).Possibly related (unconfirmed)
pytest-dev/pytest#14500 reports "Windows fatal exception: access violation (GC crash on exit)" also on Python 3.14.5, in an unrelated project. I can't confirm it's the same root cause, but it suggests Python 3.14's shutdown/GC behavior may be exposing latent bugs in multiple C-extension modules that haven't yet fully adapted, not something unique to sip. Flagging in case it's a useful cross-reference.
Expected vs actual
0xC0000005, no Python exception, insip.cp314-win_amd64.pyd, only in sessions with prior background-thread-loaded data.Fix confirmed
Upgrading
PyQt6-sip13.11.1 → 13.12.0 (pip install --upgrade PyQt6-sip==13.12.0), with no other code or environment changes, resolved it — verified by reproducing the exact "load a dataset, then close" sequence that crashed reliably before the upgrade.