From 16589503862e6850b7efaddba8dd5c1cca8109f9 Mon Sep 17 00:00:00 2001 From: DanS Date: Fri, 9 Oct 2026 21:09:48 -0500 Subject: [PATCH] fix(wallets): don't rebuild the wallet list inside its own row loop Open on a wallet row whose file was moved or deleted after the list was built rebuilt the list from inside the dialog's row loop. The loop still held a reference to that row and walked a sort order sized for the old list, so the click read freed memory and could index past the end. Open now flags a rescan for the next frame; the vanished row drops out one frame later, with no crash. Co-Authored-By: Claude Opus 5.5 --- src/ui/windows/wallets_dialog.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/ui/windows/wallets_dialog.h b/src/ui/windows/wallets_dialog.h index eb1f27c..9181a2f 100644 --- a/src/ui/windows/wallets_dialog.h +++ b/src/ui/windows/wallets_dialog.h @@ -654,8 +654,10 @@ private: // The row could be stale (file moved/deleted between the scan and this click). Don't link a ghost: // on Linux/macOS create_symlink to a missing target would happily succeed (a dangling link), we'd // switch to it, and the daemon guard would silently fall back to wallet.dat — "opened X, got the - // default". Refresh the list instead so the vanished row drops out. - if (!fs::exists(src, ec)) { scan(); return; } + // default". Refresh the list instead so the vanished row drops out — but not here: we are inside + // render()'s row loop, which holds `const WalletRow& r` and iterates s_order sized for the current + // rows, so a rebuild would leave both dangling (out-of-bounds). Flag it; the next frame rescans. + if (!fs::exists(src, ec)) { s_needScan = true; return; } // canonPath (symlinks resolved) is the wallet's stable identity: hash it so the same file reached // via any path maps to ONE link name, and point the link straight at the resolved real file. const std::string target = r.canonPath.empty() ? canonicalOf(src) : r.canonPath;