From c85272b546c15cfa175034e4a71b0bf46cf9a525 Mon Sep 17 00:00:00 2001 From: SomberNight Date: Fri, 18 Jul 2025 16:34:54 +0000 Subject: [PATCH] crash reporter: only show reporter once per exception groupid - don't show crash reporter multiple times for the "same" exception - at least until the user restart the program (there is no persistence) - this is a ~replacement for the removed "Never show crash reporter" option - in case there e.g. a thread with timer spamming an exception erroneously, this ensures we only offer to send the crash report once (per process lifecycle) --- electrum/base_crash_reporter.py | 12 ++++++++++++ electrum/gui/qml/qeapp.py | 5 +++++ electrum/gui/qt/exception_window.py | 5 +++++ 3 files changed, 22 insertions(+) diff --git a/electrum/base_crash_reporter.py b/electrum/base_crash_reporter.py index 137e7061a..1720538c4 100644 --- a/electrum/base_crash_reporter.py +++ b/electrum/base_crash_reporter.py @@ -33,6 +33,7 @@ from . import constants from .i18n import _ from .util import make_aiohttp_session, error_text_str_to_safe_str from .logging import describe_os_version, Logger, get_git_version +from .crypto import sha256 if TYPE_CHECKING: from .network import ProxySettings @@ -140,6 +141,17 @@ class BaseCrashReporter(Logger): "id": _id, } + @classmethod + def get_traceback_groupid_hash( + cls, + exctype: type[BaseException], + excvalue: BaseException, + tb: TracebackType | None, + ) -> bytes: + tb_info = cls.get_traceback_info(exctype, excvalue, tb) + _id = tb_info["id"] + return sha256(str(_id)) + def get_additional_info(self): args = { "app_version": get_git_version() or ELECTRUM_VERSION, diff --git a/electrum/gui/qml/qeapp.py b/electrum/gui/qml/qeapp.py index a999e4f1c..349f89e5c 100644 --- a/electrum/gui/qml/qeapp.py +++ b/electrum/gui/qml/qeapp.py @@ -537,6 +537,7 @@ class Exception_Hook(QObject, Logger): Logger.__init__(self) assert self._INSTANCE is None, "Exception_Hook is supposed to be a singleton" self.wallet_types_seen = set() # type: Set[str] + self.exception_ids_seen = set() # type: Set[bytes] sys.excepthook = self.handler threading.excepthook = self.handler @@ -554,4 +555,8 @@ class Exception_Hook(QObject, Logger): def handler(self, *exc_info): self.logger.error('exception caught by crash reporter', exc_info=exc_info) + groupid_hash = BaseCrashReporter.get_traceback_groupid_hash(*exc_info) + if groupid_hash in self.exception_ids_seen: + return # to avoid annoying the user, only show crash reporter once per exception groupid + self.exception_ids_seen.add(groupid_hash) self._report_exception.emit(*exc_info) diff --git a/electrum/gui/qt/exception_window.py b/electrum/gui/qt/exception_window.py index 63aa1cec1..848b97cc0 100644 --- a/electrum/gui/qt/exception_window.py +++ b/electrum/gui/qt/exception_window.py @@ -177,6 +177,7 @@ class Exception_Hook(QObject, Logger): assert self._INSTANCE is None, "Exception_Hook is supposed to be a singleton" self.config = config self.wallet_types_seen = set() # type: Set[str] + self.exception_ids_seen = set() # type: Set[bytes] sys.excepthook = self.handler self._report_exception.connect(_show_window) @@ -191,6 +192,10 @@ class Exception_Hook(QObject, Logger): def handler(self, *exc_info): self.logger.error('exception caught by crash reporter', exc_info=exc_info) + groupid_hash = BaseCrashReporter.get_traceback_groupid_hash(*exc_info) + if groupid_hash in self.exception_ids_seen: + return # to avoid annoying the user, only show crash reporter once per exception groupid + self.exception_ids_seen.add(groupid_hash) self._report_exception.emit(self.config, *exc_info)