Repository: bitcoin/bitcoin
File: src/wallet/wallet.cpp (C++)

Code before:
    }

    // In case of loading failure, we need to remember the wallet files we have created to remove.
    // A `set` is used as it may be populated with the same wallet directory paths multiple times,
    // both before and after loading. This ensures the set is complete even if one of the wallets
    // fails to load.
    std::set<fs::path> wallet_files_to_remove;
    std::set<fs::path> wallet_empty_dirs_to_remove;
    if (success) {
        Assume(!res.wallet); // We will set it here.
        // Check if the local wallet is empty after migration
        if (empty_local_wallet) {
            // This wallet has no records. We can safely remove it.
            std::vector<fs::path> paths_to_remove = local_wallet->GetDatabase().Files();
            local_wallet.reset();
            for (const auto& path_to_remove : paths_to_remove) fs::remove_all(path_to_remove);
        }

        LogInfo("Loading new wallets after migration...\n");
        // Migration successful, load all the migrated wallets.
        for (std::shared_ptr<CWallet>* wallet_ptr : {&local_wallet, &res.watchonly_wallet, &res.solvables_wallet}) {
            if (success && *wallet_ptr) {
                std::shared_ptr<CWallet>& wallet = *wallet_ptr;

                // Keep track of created database files to allow cleanup if the procedure fails.
                const auto files = wallet->GetDatabase().Files();
                wallet_files_to_remove.insert(files.begin(), files.end());
                if (wallet->GetName() != wallet_name) {
                    // Only when this is not the main wallet, it is fully safe to delete the parent path
                    // because we created the wallet during migration
                    wallet_empty_dirs_to_remove.insert(fs::PathFromString(wallet->GetDatabase().Filename()).parent_path());
                }

                // Load wallet
                assert(wallet.use_count() == 1);
                std::string wallet_name = wallet->GetName();
                wallet.reset();
                wallet = LoadWallet(context, wallet_name, /*load_on_start=*/std::nullopt, options, status, error, warnings);
                if (!wallet) {
                    LogError("Failed to load wallet '%s' after migration. Rolling back migration to preserve consistency. "
                             "Error cause: %s\n", wallet_name, error.original);
                    success = false;
                    break;
                }

                // Set the first successfully loaded wallet as the main one.
                // The loop order is intentional and must always start with the local wallet.
                if (!res.wallet) {
                    res.wallet_name = wallet->GetName();
                    res.wallet = std::move(wallet);
                }

Review comment (bug):
nit: This logic can be de-duplicated.

```diff
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 79e738fc28..9b3d343ea2 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -4349,6 +4349,18 @@ util::Result<MigrationResult> MigrateLegacyToDescriptor(std::shared_ptr<CWallet>
     // fails to load.
     std::set<fs::path> wallet_files_to_remove;
     std::set<fs::path> wallet_empty_dirs_to_remove;
+
+    // Helper to track wallet files and directories for cleanup on failure.
+    // Only directories of wallets created during migration (not the main wallet) are tracked.
+    auto track_wallet_for_cleanup = [&](const CWallet& wallet) {
+        const auto files = wallet.GetDatabase().Files();
+        wallet_files_to_remove.insert(files.begin(), files.end());
+        if (wallet.GetName() != wallet_name) {
+            wallet_empty_dirs_to_remove.insert(fs::PathFromString(wallet.GetDatabase().Filename()).parent_path());
+        }
+    };
+
+
     if (success) {
         Assume(!res.wallet); // We will set it here.
         // Check if the local wallet is empty after migration
@@ -4365,14 +4377,7 @@ util::Result<MigrationResult> MigrateLegacyToDescriptor(std::shared_ptr<CWallet>
             if (success && *wallet_ptr) {
                 std::shared_ptr<CWallet>& wallet = *wallet_ptr;
 
-                // Keep track of created database files to allow cleanup if the procedure fails.
-                const auto files = wallet->GetDatabase().Files();
-                wallet_files_to_remove.insert(files.begin(), files.end());
-                if (wallet->GetName() != wallet_name) {
-                    // Only when this is not the main wallet, it is fully safe to delete the parent path
-                    // because we created the wallet during migration
-                    wallet_empty_dirs_to_remove.insert(fs::PathFromString(wallet->GetDatabase().Filename()).parent_path());
-                }
+                track_wallet_for_cleanup(*wallet);
 
                 // Load wallet
                 assert(wallet.use_count() == 1);
@@ -4404,13 +4409,7 @@ util::Result<MigrationResult> MigrateLegacyToDescriptor(std::shared_ptr<CWallet>
 
         // Get the directories to remove after unloading
         for (std::shared_ptr<CWallet>& wallet : created_wallets) {
-            const auto files = wallet->GetDatabase().Files();
-            wallet_files_to_remove.insert(files.begin(), files.end());
-            if (wallet->GetName() != wallet_name) {
-                // Only when this is not the main wallet, it is fully safe to delete the parent path
-                // because we created the wallet during migration
-                wallet_empty_dirs_to_remove.insert(fs::PathFromString(wallet->GetDatabase().Filename()).parent_path());
-            }
+            track_wallet_for_cleanup(*wallet);
         }
 
         // Unload the wallets
```

Code after:
    }

    // In case of loading failure, we need to remember the wallet files we have created to remove.
    // A `set` is used as it may be populated with the same wallet directory paths multiple times,
    // both before and after loading. This ensures the set is complete even if one of the wallets
    // fails to load.
    std::set<fs::path> wallet_files_to_remove;
    std::set<fs::path> wallet_empty_dirs_to_remove;

    // Helper to track wallet files and directories for cleanup on failure.
    // Only directories of wallets created during migration (not the main wallet) are tracked.
    auto track_for_cleanup = [&](const CWallet& wallet) {
        const auto files = wallet.GetDatabase().Files();
        wallet_files_to_remove.insert(files.begin(), files.end());
        if (wallet.GetName() != wallet_name) {
            // If this isn’t the main wallet, mark its directory for removal.
            // This applies to the watch-only and solvable wallets.
            // Wallets stored directly as files in the top-level directory
            // (e.g. default unnamed wallets) don’t have a removable parent directory.
            wallet_empty_dirs_to_remove.insert(fs::PathFromString(wallet.GetDatabase().Filename()).parent_path());
        }
    };


    if (success) {
        Assume(!res.wallet); // We will set it here.
        // Check if the local wallet is empty after migration
        if (empty_local_wallet) {
            // This wallet has no records. We can safely remove it.
            std::vector<fs::path> paths_to_remove = local_wallet->GetDatabase().Files();
            local_wallet.reset();
            for (const auto& path_to_remove : paths_to_remove) fs::remove(path_to_remove);
        }

        LogInfo("Loading new wallets after migration...\n");
        // Migration successful, load all the migrated wallets.
        for (std::shared_ptr<CWallet>* wallet_ptr : {&local_wallet, &res.watchonly_wallet, &res.solvables_wallet}) {
            if (success && *wallet_ptr) {
                std::shared_ptr<CWallet>& wallet = *wallet_ptr;
                // Track db path and load wallet
                track_for_cleanup(*wallet);
                assert(wallet.use_count() == 1);
                std::string wallet_name = wallet->GetName();
                wallet.reset();
                wallet = LoadWallet(context, wallet_name, /*load_on_start=*/std::nullopt, options, status, error, warnings);
                if (!wallet) {
                    LogError("Failed to load wallet '%s' after migration. Rolling back migration to preserve consistency. "
                             "Error cause: %s\n", wallet_name, error.original);
                    success = false;
                    break;
                }