MainWindow._show_about() runs AboutDialog(self).exec() on a temporary. AboutDialog._check_for_updates() stores the running worker only on the dialog (self._worker = UpdateCheckWorker()). If the user clicks Check for updates and closes the dialog before the network call finishes (timeout is 4 s, so the window is real on a slow connection), the dialog — and with it the running QThread — is garbage-collected. Qt aborts the process with QThread: Destroyed while thread is still running.
Also: _on_check_done sets self._worker = None from the worker's own done signal; the thread may not have fully finished when the last reference drops. Same crash class.
Proposed fix
Parent the worker to the MainWindow (or keep it on a longer-lived owner), or
in AboutDialog.closeEvent/done(), if a worker is running: disconnect the signal and wait() (bounded) before dropping the reference; and in _on_check_done, self._worker.deleteLater() after finished rather than dropping the Python reference immediately.
Acceptance
Open About → Check for updates → immediately close the dialog, with the network artificially slowed (e.g. monkeypatched fetch_latest_release sleeping 3 s): no QThread: Destroyed while thread is still running, no crash.
Normal check flow still updates the label and shows the release button.
**Found in audit of `main` @ 06326e5.**
`MainWindow._show_about()` runs `AboutDialog(self).exec()` on a temporary. `AboutDialog._check_for_updates()` stores the running worker only on the dialog (`self._worker = UpdateCheckWorker()`). If the user clicks **Check for updates** and closes the dialog before the network call finishes (timeout is 4 s, so the window is real on a slow connection), the dialog — and with it the running `QThread` — is garbage-collected. Qt aborts the process with `QThread: Destroyed while thread is still running`.
Anchors: `bcc.py` — `_show_about` (~L1503), `AboutDialog._check_for_updates` (~L1397), `UpdateCheckWorker` (~L166).
**Also**: `_on_check_done` sets `self._worker = None` from the worker's own `done` signal; the thread may not have fully finished when the last reference drops. Same crash class.
**Proposed fix**
- Parent the worker to the MainWindow (or keep it on a longer-lived owner), or
- in `AboutDialog.closeEvent`/`done()`, if a worker is running: disconnect the signal and `wait()` (bounded) before dropping the reference; and in `_on_check_done`, `self._worker.deleteLater()` after `finished` rather than dropping the Python reference immediately.
**Acceptance**
- Open About → Check for updates → immediately close the dialog, with the network artificially slowed (e.g. monkeypatched `fetch_latest_release` sleeping 3 s): no `QThread: Destroyed while thread is still running`, no crash.
- Normal check flow still updates the label and shows the release button.
the_og
added the P1 label 2026-07-12 12:43:53 -04:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Found in audit of
main@06326e5.MainWindow._show_about()runsAboutDialog(self).exec()on a temporary.AboutDialog._check_for_updates()stores the running worker only on the dialog (self._worker = UpdateCheckWorker()). If the user clicks Check for updates and closes the dialog before the network call finishes (timeout is 4 s, so the window is real on a slow connection), the dialog — and with it the runningQThread— is garbage-collected. Qt aborts the process withQThread: Destroyed while thread is still running.Anchors:
bcc.py—_show_about(~L1503),AboutDialog._check_for_updates(~L1397),UpdateCheckWorker(~L166).Also:
_on_check_donesetsself._worker = Nonefrom the worker's owndonesignal; the thread may not have fully finished when the last reference drops. Same crash class.Proposed fix
AboutDialog.closeEvent/done(), if a worker is running: disconnect the signal andwait()(bounded) before dropping the reference; and in_on_check_done,self._worker.deleteLater()afterfinishedrather than dropping the Python reference immediately.Acceptance
fetch_latest_releasesleeping 3 s): noQThread: Destroyed while thread is still running, no crash.