After Rob's wallet-creation finding, I created a Windows VM and asked Codex desktop to try 500 GUI scenarios it considered important. This finding came from that exploration.
This is a locally reproduced issue, the details were given to an AI to convert into an issue, treat everything as a hint to investigate, not as final truth.
Current behaviour
Transaction details windows remain open after their wallet is closed. Closing one of those detached windows can crash bitcoin-qt.
In the Windows VM exploration, two transaction details windows remained readable after their wallet was closed. Closing one was the last GUI action before bitcoin-qt disappeared. The Windows Application log records an access violation in PID 7888 at 2026-09-07 06:07:16 UTC:
Faulting application: bitcoin-qt.exe 31.99.0.0
Exception code: 0xc0000005
Fault offset: 0x0000000000108d29
Application path: C:\Users\Public\bitcoin-gui-260907b\bin\bitcoin-qt.exe
The debug log ends after Releasing wallet encrypted-restored-155.. and has no clean-shutdown entry.
The Windows dump was not symbolized, but a focused AddressSanitizer reproduction of the same lifetime sequence on macOS confirms a heap-use-after-free. TransactionView is deleted while its details window remains alive. When the details window is later destroyed, its callback calls m_opened_dialogs.removeOne() through the deleted TransactionView.
Expected behaviour
Closing a wallet should close its transaction details windows. Destroying a details window must not invoke a callback whose TransactionView has already been destroyed.
Steps to reproduce
Grouped regtest setup and manual reproduction
From an unpatched source checkout with the GUI build dependencies installed, run the following in bash or zsh. It launches a fresh regtest instance, creates a wallet, and adds a transaction that can be opened in the GUI.
{ cmake -B build-gui-wallet-unload -DBUILD_GUI=ON && cmake --build build-gui-wallet-unload -j10 --target bitcoin-qt bitcoin-cli; } >/dev/null 2>&1 &&
{
gui_datadir=$(mktemp -d)
gui_rpc() { build-gui-wallet-unload/bin/bitcoin-cli -regtest -datadir="$gui_datadir" -rpcport=19577 "$@"; }
build-gui-wallet-unload/bin/bitcoin-qt -regtest -datadir="$gui_datadir" -server -rpcport=19577 \
-noconnect -nolisten -discover=0 -fallbackfee=0.00001 >"$gui_datadir/gui.log" 2>&1 &
gui_rpc -rpcwait -rpcwaittimeout=30 createwallet repro &&
gui_address=$(gui_rpc -rpcwallet=repro getnewaddress) &&
gui_rpc -rpcwallet=repro generatetoaddress 101 "$gui_address" >/dev/null &&
gui_rpc -rpcwallet=repro sendtoaddress "$gui_address" 1 >/dev/null &&
gui_rpc -rpcwallet=repro generatetoaddress 1 "$gui_address" >/dev/null
}
In the GUI:
- Open
Transactions and double-click the Sent to entry for the 1 BTC payment.
- Leave the transaction details open.
- Choose
File > Close Wallet and confirm closing repro.
- Observe that the transaction details remain open, then close that window.
The native crash may depend on when Qt processes the deferred deletion. Running the same sequence under AddressSanitizer reports the heap-use-after-free directly.
Stop the temporary instance when finished:
How did you obtain Bitcoin Core?
Compiled from source.
What version of Bitcoin Core are you using?
master@a174dd4017cf97f283b1848bb31998bfa28465e9.
Operating system and version
The native crash occurred on Windows Server 2025 Datacenter 10.0.26100 x64 in a VM with Qt 6.11.2. The AddressSanitizer reproduction used macOS 26.5, Qt 6.11.2 and Apple LLVM 21.
Additional context
TransactionView::showDetails() creates each TransactionDescDialog as a top-level window and stores it in m_opened_dialogs. Its destroyed callback captures this without a QObject context. Closing a wallet deletes WalletView and its TransactionView, but the top-level details windows survive. Destroying one later runs the callback against the freed view.
The focused AddressSanitizer trace reports the invalid access in this path:
QList<TransactionDescDialog*>::removeOne()
TransactionView::showDetails()::$_0::operator()()
QObject::destroyed(QObject*)
TransactionDescDialog::~TransactionDescDialog()
The closest existing work I found is related but does not fix this lifetime:
- #941 closes details when switching wallets, but it does not close them from
TransactionView destruction or add a lifetime context to the callback.
- #817 limits multiple details windows for one transaction, but retains the callback without a context.
- #835 fixed a different crash caused by removing the same wallet model twice.
Suggested patch (optional)
This is the lifetime part of the local patch and is a possible starting point. Closing the owned dialogs handles the visible stale windows, while using the view as the callback context prevents the callback from outliving it.
diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp
index 0cb9b43b84..cf753f5c73 100644
--- a/src/qt/transactionview.cpp
+++ b/src/qt/transactionview.cpp
@@ -191,6 +191,7 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa
TransactionView::~TransactionView()
{
+ closeOpenedDialogs();
QSettings settings;
// Rename this cache when adding or removing columns.
settings.setValue("TransactionViewHeaderState-2025", transactionView->horizontalHeader()->saveState());
@@ -501,7 +502,7 @@ void TransactionView::showDetails()
TransactionDescDialog *dlg = new TransactionDescDialog(selection.at(0));
dlg->setAttribute(Qt::WA_DeleteOnClose);
m_opened_dialogs.append(dlg);
- connect(dlg, &QObject::destroyed, [this, dlg] {
+ connect(dlg, &QObject::destroyed, this, [this, dlg] {
m_opened_dialogs.removeOne(dlg);
});
dlg->show();
After Rob's wallet-creation finding, I created a Windows VM and asked Codex desktop to try 500 GUI scenarios it considered important. This finding came from that exploration.
This is a locally reproduced issue, the details were given to an AI to convert into an issue, treat everything as a hint to investigate, not as final truth.
Current behaviour
Transaction details windows remain open after their wallet is closed. Closing one of those detached windows can crash
bitcoin-qt.In the Windows VM exploration, two transaction details windows remained readable after their wallet was closed. Closing one was the last GUI action before
bitcoin-qtdisappeared. The Windows Application log records an access violation in PID 7888 at 2026-09-07 06:07:16 UTC:The debug log ends after
Releasing wallet encrypted-restored-155..and has no clean-shutdown entry.The Windows dump was not symbolized, but a focused AddressSanitizer reproduction of the same lifetime sequence on macOS confirms a heap-use-after-free.
TransactionViewis deleted while its details window remains alive. When the details window is later destroyed, its callback callsm_opened_dialogs.removeOne()through the deletedTransactionView.Expected behaviour
Closing a wallet should close its transaction details windows. Destroying a details window must not invoke a callback whose
TransactionViewhas already been destroyed.Steps to reproduce
Grouped regtest setup and manual reproduction
From an unpatched source checkout with the GUI build dependencies installed, run the following in bash or zsh. It launches a fresh regtest instance, creates a wallet, and adds a transaction that can be opened in the GUI.
{ cmake -B build-gui-wallet-unload -DBUILD_GUI=ON && cmake --build build-gui-wallet-unload -j10 --target bitcoin-qt bitcoin-cli; } >/dev/null 2>&1 && { gui_datadir=$(mktemp -d) gui_rpc() { build-gui-wallet-unload/bin/bitcoin-cli -regtest -datadir="$gui_datadir" -rpcport=19577 "$@"; } build-gui-wallet-unload/bin/bitcoin-qt -regtest -datadir="$gui_datadir" -server -rpcport=19577 \ -noconnect -nolisten -discover=0 -fallbackfee=0.00001 >"$gui_datadir/gui.log" 2>&1 & gui_rpc -rpcwait -rpcwaittimeout=30 createwallet repro && gui_address=$(gui_rpc -rpcwallet=repro getnewaddress) && gui_rpc -rpcwallet=repro generatetoaddress 101 "$gui_address" >/dev/null && gui_rpc -rpcwallet=repro sendtoaddress "$gui_address" 1 >/dev/null && gui_rpc -rpcwallet=repro generatetoaddress 1 "$gui_address" >/dev/null }In the GUI:
Transactionsand double-click theSent toentry for the 1 BTC payment.File>Close Walletand confirm closingrepro.The native crash may depend on when Qt processes the deferred deletion. Running the same sequence under AddressSanitizer reports the heap-use-after-free directly.
Stop the temporary instance when finished:
How did you obtain Bitcoin Core?
Compiled from source.
What version of Bitcoin Core are you using?
master@a174dd4017cf97f283b1848bb31998bfa28465e9.
Operating system and version
The native crash occurred on Windows Server 2025 Datacenter 10.0.26100 x64 in a VM with Qt 6.11.2. The AddressSanitizer reproduction used macOS 26.5, Qt 6.11.2 and Apple LLVM 21.
Additional context
TransactionView::showDetails()creates eachTransactionDescDialogas a top-level window and stores it inm_opened_dialogs. Itsdestroyedcallback capturesthiswithout a QObject context. Closing a wallet deletesWalletViewand itsTransactionView, but the top-level details windows survive. Destroying one later runs the callback against the freed view.The focused AddressSanitizer trace reports the invalid access in this path:
The closest existing work I found is related but does not fix this lifetime:
TransactionViewdestruction or add a lifetime context to the callback.Suggested patch (optional)
This is the lifetime part of the local patch and is a possible starting point. Closing the owned dialogs handles the visible stale windows, while using the view as the callback context prevents the callback from outliving it.