Skip to content

fix(accounts): prevent crash when account tree references orphaned account IDs#41405

Merged
gantunesr merged 4 commits into
mainfrom
fix/account-tree-tolowercase-state-corruption
Apr 2, 2026
Merged

fix(accounts): prevent crash when account tree references orphaned account IDs#41405
gantunesr merged 4 commits into
mainfrom
fix/account-tree-tolowercase-state-corruption

Conversation

@gantunesr

@gantunesr gantunesr commented Mar 31, 2026

Copy link
Copy Markdown
Member

Description

When state corruption occurs (e.g. after a wallet reset or Snap keyring reset), the accountTree can contain account IDs that no longer exist in internalAccounts. In getWalletsWithAccounts, spreading undefined via { ...accountsById[missingId] } produces an empty object with no address property. Downstream, getWalletIdAndNameByAccountAddress then calls .toLowerCase() on undefined, crashing with:

TypeError: Cannot read properties of undefined (reading 'toLowerCase')
  at getWalletIdAndNameByAccountAddress (ui/selectors/multichain-accounts/account-tree.ts)

Root cause: The account tree references entropy source IDs that no longer exist in the actual keyrings — typically after a user forgets their password and uses the wallet reset flow, or after using Snaps.

Fix:

  1. getWalletsWithAccounts — added a .filter() before .map() to skip any account ID that has no entry in accountsById. This prevents orphaned entries from ever entering the consolidated wallet data structure.
  2. getWalletIdAndNameByAccountAddress — added optional chaining acc.address?.toLowerCase() as a defensive guard.
  3. Three regression tests added to prevent future regressions.

Changelog

CHANGELOG entry: Fixed a crash that could occur for users with corrupted wallet state after a password reset or Snap keyring usage.

Related issues

Fixes: #41141
Fixes: https://consensyssoftware.atlassian.net/browse/MUL-1633

Manual testing steps

This bug is triggered by corrupted state, which is hard to reproduce manually. The fix is covered by unit tests. To verify:

  1. Run yarn test:unit ui/selectors/multichain-accounts/account-tree.test.ts — all 75 tests should pass.
  2. Load the extension normally and verify the account list renders without errors.

Pre-merge author checklist

Pre-merge reviewer checklist

  • I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed).
  • I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots.

Made with Cursor


Note

Low Risk
Low risk defensive change in selectors: it only filters out account IDs that cannot be resolved to internalAccounts, preventing crashes in downstream lookups. Main impact is that corrupted/stale entries will no longer appear in consolidated wallet/group account lists.

Overview
Prevents crashes when accountTree contains stale/orphaned account IDs by filtering unresolved IDs out of getWalletsWithAccounts before building the consolidated wallets/groups structure.

Adds regression tests covering: filtering orphaned IDs from groups, getWalletIdAndNameByAccountAddress not throwing with corrupted state, and returning null when all referenced accounts are orphaned.

Written by Cursor Bugbot for commit d779609. This will update automatically on new commits. Configure here.

…count IDs

When state corruption occurs (e.g. after a wallet reset or Snap keyring
reset), the accountTree can contain account IDs that no longer exist in
internalAccounts. Spreading `undefined` via `{ ...accountsById[id] }`
produced an empty object with no `address`, causing
`getWalletIdAndNameByAccountAddress` to throw
`TypeError: Cannot read properties of undefined (reading 'toLowerCase')`.

Fix by filtering orphaned account IDs out of each group in
`getWalletsWithAccounts` so corrupted entries never propagate, and add
optional chaining on `acc.address` in `getWalletIdAndNameByAccountAddress`
as a defensive guard. Adds three regression tests.

Fixes: #41141
Made-with: Cursor
@metamaskbotv2

metamaskbotv2 Bot commented Mar 31, 2026

Copy link
Copy Markdown
Contributor

✨ Files requiring CODEOWNER review ✨

🔑 @MetaMask/accounts-engineers (2 files, +124 -16)
  • 📁 ui/
    • 📁 selectors/
      • 📁 multichain-accounts/
        • 📄 account-tree.test.ts +106 -0
        • 📄 account-tree.ts +18 -16

@gantunesr gantunesr marked this pull request as ready for review March 31, 2026 22:47
@gantunesr gantunesr requested a review from a team as a code owner March 31, 2026 22:47
@metamaskbotv2

metamaskbotv2 Bot commented Mar 31, 2026

Copy link
Copy Markdown
Contributor
Builds ready [72dff8c]
⚡ Performance Benchmarks (Total: 🟢 18 pass · 🟡 0 warn · 🔴 0 fail)

Baseline (latest main): 806ac67 | Date: 4/23/58217 | Pipeline: 23822964247 | Baseline logs

Interaction Benchmarks
Benchmarkchrome-browserify
loadNewAccount🟢 [Show logs]
confirmTx🟢 [Show logs]
bridgeUserActions🟢 [Show logs]

📈 Results compared to the previous 5 runs on main

  • bridgeUserActions/bridge_load_page: -11%
  • bridgeUserActions/bridge_search_token: -20%
  • bridgeUserActions/total: -16%
Startup Benchmarks
Benchmarkchrome-browserifychrome-webpackfirefox-browserifyfirefox-webpack
startupStandardHome🟢 [Show logs]🟢 [Show logs]🟢 [Show logs]🟢 [Show logs]
startupPowerUserHome🟢 [Show logs]🟢 [Show logs]🟢 [Show logs]🟢 [Show logs]

📈 Results compared to the previous 5 runs on main

  • startupStandardHome/firstPaint: -12%
  • startupStandardHome/initialActions: +11%
  • startupPowerUserHome/backgroundConnect: +47%
  • startupPowerUserHome/numNetworkReqs: -27%
  • startupPowerUserHome/setupStore: -14%
  • startupPowerUserHome/numNetworkReqs: +55%
  • startupStandardHome/domInteractive: -12%
  • startupStandardHome/initialActions: +25%
  • startupPowerUserHome/uiStartup: -12%
  • startupPowerUserHome/domInteractive: -33%
  • startupPowerUserHome/backgroundConnect: -17%
  • startupPowerUserHome/loadScripts: -12%
  • startupStandardHome/initialActions: +25%
  • startupStandardHome/setupStore: +82%
User Journey Benchmarks
Benchmarkchrome-browserify
onboardingImportWallet🟢 [Show logs]
onboardingNewWallet🟢 [Show logs]
assetDetails🟢 [Show logs]
solanaAssetDetails🟢 [Show logs]
importSrpHome🟢 [Show logs]
sendTransactions🟢 [Show logs]
swap🟢 [Show logs]

📈 Results compared to the previous 5 runs on main

  • onboardingImportWallet/pwFormToMetricsScreen: +11%
  • onboardingImportWallet/doneButtonToHomeScreen: -76%
  • onboardingImportWallet/openAccountMenuToAccountListLoaded: +37%
  • onboardingImportWallet/total: -38%
  • onboardingNewWallet/agreeButtonToOnboardingSuccess: +31%
  • onboardingNewWallet/doneButtonToAssetList: -38%
  • onboardingNewWallet/total: -32%
  • assetDetails/assetClickToPriceChart: -65%
  • assetDetails/total: -65%
  • solanaAssetDetails/assetClickToPriceChart: -60%
  • solanaAssetDetails/total: -60%
  • importSrpHome/loginToHomeScreen: +13%
  • importSrpHome/openAccountMenuAfterLogin: -28%
  • importSrpHome/homeAfterImportWithNewWallet: -27%
  • importSrpHome/total: -22%
  • swap/openSwapPageFromHome: -84%
  • swap/fetchAndDisplaySwapQuotes: +30%
🌐 Dapp Page Load Benchmarks

Current Commit: 72dff8c | Date: 3/31/2026

📄 Localhost MetaMask Test Dapp

Samples: 100

Summary

  • pageLoadTime-> current mean value: 1.03s (±38ms) 🟡 | historical mean value: 1.04s ⬇️ (historical data)
  • domContentLoaded-> current mean value: 721ms (±36ms) 🟢 | historical mean value: 726ms ⬇️ (historical data)
  • firstContentfulPaint-> current mean value: 85ms (±11ms) 🟢 | historical mean value: 87ms ⬇️ (historical data)

📈 Detailed Results

Metric Mean Std Dev Min Max P95 P99
pageLoadTime 1.03s 38ms 999ms 1.31s 1.05s 1.31s
domContentLoaded 721ms 36ms 697ms 992ms 748ms 992ms
firstPaint 85ms 11ms 72ms 168ms 96ms 168ms
firstContentfulPaint 85ms 11ms 72ms 168ms 96ms 168ms
largestContentfulPaint 0ms 0ms 0ms 0ms 0ms 0ms
Bundle size diffs [🚨 Warning! Bundle size has increased!]
  • background: 58 Bytes (0%)
  • ui: 1.15 KiB (0.01%)
  • common: 119 Bytes (0%)

Comment thread ui/selectors/multichain-accounts/account-tree.ts Outdated
@metamaskbotv2

metamaskbotv2 Bot commented Apr 1, 2026

Copy link
Copy Markdown
Contributor
Builds ready [a11a9ef]
⚡ Performance Benchmarks (Total: 🟢 18 pass · 🟡 0 warn · 🔴 0 fail)

Baseline (latest main): 806ac67 | Date: 4/23/58217 | Pipeline: 23824728689 | Baseline logs

Interaction Benchmarks
Benchmarkchrome-browserify
loadNewAccount🟢 [Show logs]
confirmTx🟢 [Show logs]
bridgeUserActions🟢 [Show logs]

📈 Results compared to the previous 5 runs on main

  • loadNewAccount/load_new_account: +11%
  • loadNewAccount/total: +11%
  • bridgeUserActions/bridge_search_token: -17%
Startup Benchmarks
Benchmarkchrome-browserifychrome-webpackfirefox-browserifyfirefox-webpack
startupStandardHome🟢 [Show logs]🟢 [Show logs]🟢 [Show logs]🟢 [Show logs]
startupPowerUserHome🟢 [Show logs]🟢 [Show logs]🟢 [Show logs]🟢 [Show logs]

📈 Results compared to the previous 5 runs on main

  • startupStandardHome/initialActions: +11%
  • startupPowerUserHome/backgroundConnect: -18%
  • startupPowerUserHome/numNetworkReqs: +49%
  • startupStandardHome/firstPaint: -21%
  • startupStandardHome/firstReactRender: -11%
  • startupPowerUserHome/numNetworkReqs: -13%
  • startupStandardHome/domInteractive: +20%
  • startupStandardHome/initialActions: +25%
  • startupStandardHome/setupStore: -11%
  • startupPowerUserHome/setupStore: +12%
  • startupStandardHome/initialActions: +25%
  • startupStandardHome/setupStore: -17%
  • startupPowerUserHome/domInteractive: -15%
  • startupPowerUserHome/setupStore: +18%
User Journey Benchmarks
Benchmarkchrome-browserify
onboardingImportWallet🟢 [Show logs]
onboardingNewWallet🟢 [Show logs]
assetDetails🟢 [Show logs]
solanaAssetDetails🟢 [Show logs]
importSrpHome🟢 [Show logs]
sendTransactions🟢 [Show logs]
swap🟢 [Show logs]

📈 Results compared to the previous 5 runs on main

  • onboardingImportWallet/pwFormToMetricsScreen: +44%
  • onboardingImportWallet/doneButtonToHomeScreen: -75%
  • onboardingImportWallet/openAccountMenuToAccountListLoaded: +50%
  • onboardingImportWallet/total: -33%
  • onboardingNewWallet/skipBackupToMetricsScreen: +10%
  • onboardingNewWallet/agreeButtonToOnboardingSuccess: -13%
  • onboardingNewWallet/doneButtonToAssetList: -35%
  • onboardingNewWallet/total: -29%
  • assetDetails/assetClickToPriceChart: -56%
  • assetDetails/total: -56%
  • solanaAssetDetails/assetClickToPriceChart: -50%
  • solanaAssetDetails/total: -50%
  • importSrpHome/openAccountMenuAfterLogin: -28%
  • importSrpHome/homeAfterImportWithNewWallet: -27%
  • importSrpHome/total: -24%
  • swap/openSwapPageFromHome: -85%
  • swap/fetchAndDisplaySwapQuotes: +30%
🌐 Dapp Page Load Benchmarks

Current Commit: a11a9ef | Date: 4/1/2026

📄 Localhost MetaMask Test Dapp

Samples: 100

Summary

  • pageLoadTime-> current mean value: 992ms (±46ms) 🟢 | historical mean value: 1.04s ⬇️ (historical data)
  • domContentLoaded-> current mean value: 697ms (±42ms) 🟢 | historical mean value: 726ms ⬇️ (historical data)
  • firstContentfulPaint-> current mean value: 83ms (±12ms) 🟢 | historical mean value: 87ms ⬇️ (historical data)

📈 Detailed Results

Metric Mean Std Dev Min Max P95 P99
pageLoadTime 992ms 46ms 961ms 1.32s 1.02s 1.32s
domContentLoaded 697ms 42ms 669ms 1.00s 729ms 1.00s
firstPaint 83ms 12ms 64ms 180ms 92ms 180ms
firstContentfulPaint 83ms 12ms 64ms 180ms 92ms 180ms
largestContentfulPaint 0ms 0ms 0ms 0ms 0ms 0ms
Bundle size diffs
  • background: 58 Bytes (0%)
  • ui: 34 Bytes (0%)
  • common: 20 Bytes (0%)

The filter in getWalletsWithAccounts already guarantees every account in
the consolidated wallet data has a valid address, so the ?. guard on
acc.address in getWalletIdAndNameByAccountAddress is not needed.

Made-with: Cursor
@metamaskbotv2

metamaskbotv2 Bot commented Apr 1, 2026

Copy link
Copy Markdown
Contributor
Builds ready [b72ce5d]
⚡ Performance Benchmarks (Total: 🟢 18 pass · 🟡 0 warn · 🔴 0 fail)

Baseline (latest main): 37a96ca | Date: 10/31/58218 | Pipeline: 23847362272 | Baseline logs

Interaction Benchmarks
Benchmarkchrome-browserify
loadNewAccount🟢 [Show logs]
confirmTx🟢 [Show logs]
bridgeUserActions🟢 [Show logs]

📈 Results compared to the previous 5 runs on main

  • loadNewAccount/load_new_account: +11%
  • loadNewAccount/total: +11%
  • bridgeUserActions/bridge_load_asset_picker: -19%
  • bridgeUserActions/bridge_search_token: +12%
Startup Benchmarks
Benchmarkchrome-browserifychrome-webpackfirefox-browserifyfirefox-webpack
startupStandardHome🟢 [Show logs]🟢 [Show logs]🟢 [Show logs]🟢 [Show logs]
startupPowerUserHome🟢 [Show logs]🟢 [Show logs]🟢 [Show logs]🟢 [Show logs]

📈 Results compared to the previous 5 runs on main

  • startupStandardHome/initialActions: -29%
  • startupPowerUserHome/numNetworkReqs: +53%
  • startupStandardHome/firstPaint: -14%
  • startupStandardHome/domInteractive: +19%
  • startupStandardHome/initialActions: +67%
  • startupStandardHome/setupStore: +15%
  • startupPowerUserHome/backgroundConnect: +19%
  • startupPowerUserHome/setupStore: -46%
  • startupStandardHome/initialActions: +11%
  • startupStandardHome/setupStore: -19%
  • startupPowerUserHome/setupStore: +15%
  • startupPowerUserHome/numNetworkReqs: -11%
User Journey Benchmarks
Benchmarkchrome-browserify
onboardingImportWallet🟢 [Show logs]
onboardingNewWallet🟢 [Show logs]
assetDetails🟢 [Show logs]
solanaAssetDetails🟢 [Show logs]
importSrpHome🟢 [Show logs]
sendTransactions🟢 [Show logs]
swap🟢 [Show logs]

📈 Results compared to the previous 5 runs on main

  • onboardingImportWallet/doneButtonToHomeScreen: -76%
  • onboardingImportWallet/openAccountMenuToAccountListLoaded: +56%
  • onboardingImportWallet/total: -36%
  • onboardingNewWallet/agreeButtonToOnboardingSuccess: +50%
  • onboardingNewWallet/doneButtonToAssetList: -38%
  • onboardingNewWallet/total: -31%
  • assetDetails/assetClickToPriceChart: -65%
  • assetDetails/total: -65%
  • solanaAssetDetails/assetClickToPriceChart: -46%
  • solanaAssetDetails/total: -46%
  • importSrpHome/loginToHomeScreen: +19%
  • importSrpHome/openAccountMenuAfterLogin: -52%
  • importSrpHome/homeAfterImportWithNewWallet: -37%
  • importSrpHome/total: -31%
  • swap/openSwapPageFromHome: -87%
  • swap/fetchAndDisplaySwapQuotes: +31%
  • swap/total: +11%
🌐 Dapp Page Load Benchmarks

Current Commit: b72ce5d | Date: 4/1/2026

📄 Localhost MetaMask Test Dapp

Samples: 100

Summary

  • pageLoadTime-> current mean value: 1.01s (±39ms) 🟡 | historical mean value: 1.04s ⬇️ (historical data)
  • domContentLoaded-> current mean value: 710ms (±37ms) 🟢 | historical mean value: 731ms ⬇️ (historical data)
  • firstContentfulPaint-> current mean value: 82ms (±9ms) 🟢 | historical mean value: 85ms ⬇️ (historical data)

📈 Detailed Results

Metric Mean Std Dev Min Max P95 P99
pageLoadTime 1.01s 39ms 982ms 1.31s 1.03s 1.31s
domContentLoaded 710ms 37ms 685ms 999ms 726ms 999ms
firstPaint 82ms 9ms 64ms 144ms 96ms 144ms
firstContentfulPaint 82ms 9ms 64ms 144ms 96ms 144ms
largestContentfulPaint 0ms 0ms 0ms 0ms 0ms 0ms
Bundle size diffs [🚀 Bundle size reduced!]
  • background: 58 Bytes (0%)
  • ui: -78.37 KiB (-0.93%)
  • common: 807 Bytes (0.01%)

@gantunesr gantunesr requested a review from hmalik88 April 1, 2026 12:38
@sonarqubecloud

sonarqubecloud Bot commented Apr 1, 2026

Copy link
Copy Markdown

@metamaskbotv2

metamaskbotv2 Bot commented Apr 1, 2026

Copy link
Copy Markdown
Contributor
Builds ready [d779609]
⚡ Performance Benchmarks (Total: 🟢 18 pass · 🟡 0 warn · 🔴 0 fail)

Baseline (latest main): 37a96ca | Date: 10/31/58218 | Pipeline: 23848997958 | Baseline logs

Interaction Benchmarks
Benchmarkchrome-browserify
loadNewAccount🟢 [Show logs]
confirmTx🟢 [Show logs]
bridgeUserActions🟢 [Show logs]

📈 Results compared to the previous 5 runs on main

  • loadNewAccount/load_new_account: -50%
  • loadNewAccount/total: -50%
  • bridgeUserActions/bridge_load_asset_picker: -20%
  • bridgeUserActions/total: -19%
Startup Benchmarks
Benchmarkchrome-browserifychrome-webpackfirefox-browserifyfirefox-webpack
startupStandardHome🟢 [Show logs]🟢 [Show logs]🟢 [Show logs]🟢 [Show logs]
startupPowerUserHome🟢 [Show logs]🟢 [Show logs]🟢 [Show logs]🟢 [Show logs]

📈 Results compared to the previous 5 runs on main

  • startupStandardHome/initialActions: -29%
  • startupPowerUserHome/backgroundConnect: -27%
  • startupPowerUserHome/numNetworkReqs: +43%
  • startupStandardHome/setupStore: -13%
  • startupPowerUserHome/firstPaint: -10%
  • startupPowerUserHome/numNetworkReqs: -10%
  • startupStandardHome/backgroundConnect: +14%
  • startupStandardHome/initialActions: +67%
  • startupStandardHome/setupStore: +23%
  • startupPowerUserHome/backgroundConnect: -13%
  • startupPowerUserHome/setupStore: +24%
  • startupStandardHome/backgroundConnect: -12%
  • startupStandardHome/initialActions: -44%
  • startupStandardHome/setupStore: -15%
  • startupPowerUserHome/domInteractive: +14%
  • startupPowerUserHome/backgroundConnect: +12%
  • startupPowerUserHome/firstReactRender: +14%
  • startupPowerUserHome/setupStore: -19%
User Journey Benchmarks
Benchmarkchrome-browserify
onboardingImportWallet🟢 [Show logs]
onboardingNewWallet🟢 [Show logs]
assetDetails🟢 [Show logs]
solanaAssetDetails🟢 [Show logs]
importSrpHome🟢 [Show logs]
sendTransactions🟢 [Show logs]
swap🟢 [Show logs]

📈 Results compared to the previous 5 runs on main

  • onboardingImportWallet/metricsToWalletReadyScreen: -42%
  • onboardingImportWallet/doneButtonToHomeScreen: -76%
  • onboardingImportWallet/openAccountMenuToAccountListLoaded: +44%
  • onboardingImportWallet/total: -38%
  • onboardingNewWallet/agreeButtonToOnboardingSuccess: +12%
  • onboardingNewWallet/doneButtonToAssetList: -40%
  • onboardingNewWallet/total: -32%
  • assetDetails/assetClickToPriceChart: -52%
  • assetDetails/total: -52%
  • solanaAssetDetails/assetClickToPriceChart: -47%
  • solanaAssetDetails/total: -47%
  • importSrpHome/loginToHomeScreen: +13%
  • importSrpHome/openAccountMenuAfterLogin: -52%
  • importSrpHome/homeAfterImportWithNewWallet: -39%
  • importSrpHome/total: -33%
  • swap/openSwapPageFromHome: -87%
  • swap/fetchAndDisplaySwapQuotes: +31%
  • swap/total: +11%
🌐 Dapp Page Load Benchmarks

Current Commit: d779609 | Date: 4/1/2026

📄 Localhost MetaMask Test Dapp

Samples: 100

Summary

  • pageLoadTime-> current mean value: 1.01s (±175ms) 🟡 | historical mean value: 1.04s ⬇️ (historical data)
  • domContentLoaded-> current mean value: 710ms (±172ms) 🟢 | historical mean value: 731ms ⬇️ (historical data)
  • firstContentfulPaint-> current mean value: 97ms (±150ms) 🟢 | historical mean value: 85ms ⬆️ (historical data)

📈 Detailed Results

Metric Mean Std Dev Min Max P95 P99
pageLoadTime 1.01s 175ms 954ms 2.72s 1.03s 2.72s
domContentLoaded 710ms 172ms 666ms 2.40s 733ms 2.40s
firstPaint 97ms 150ms 60ms 1.59s 96ms 1.59s
firstContentfulPaint 97ms 150ms 60ms 1.59s 96ms 1.59s
largestContentfulPaint 0ms 0ms 0ms 0ms 0ms 0ms
Bundle size diffs
  • background: 58 Bytes (0%)
  • ui: 33 Bytes (0%)
  • common: 20 Bytes (0%)

@gantunesr gantunesr added this pull request to the merge queue Apr 2, 2026
Merged via the queue into main with commit 7559485 Apr 2, 2026
207 checks passed
@gantunesr gantunesr deleted the fix/account-tree-tolowercase-state-corruption branch April 2, 2026 12:54
@github-actions github-actions Bot locked and limited conversation to collaborators Apr 2, 2026
@metamaskbot metamaskbot added the release-13.26.0 Issue or pull request that will be included in release 13.26.0 label Apr 2, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

release-13.26.0 Issue or pull request that will be included in release 13.26.0 size-M team-accounts-framework Accounts team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TypeError: Cannot read properties of undefined (reading 'toLowerCase')

4 participants