Commit 0053df5
authored
feat: add workarounds for database corruption scenarios (#39010)
## **Description**
This PR implements workarounds for Firefox database corruption issues
that cause `browser.storage.local` operations to fail with "An
unexpected error occurred".
### Problem
Firefox stores extension data in SQLite databases with external files
for large blobs. Some corruption scenarios, like [missing or renamed
external files (file IDs
mismatch)](#9196 (comment)),
can break these databases.
When this happens, `browser.storage.local.get()` or
`browser.storage.local.set()` fail entirely, leaving users locked out of
their wallet:
- #10091
- #35681
### Solution
This PR adds two recovery mechanisms:
1. **Vault Recovery on `get()` failure**: When
`browser.storage.local.get()` fails and a backup vault exists in
IndexedDB, users are redirected to the vault recovery flow to restore
their wallet.
2. **Informative Toast on `set()` failure**: When
`browser.storage.local.set()` fails (even after vault recovery), a
persistent toast is displayed informing users that the storage is
failing and MetaMask needs to be reinstalled. The toast includes a link
to save the Secret Recovery Phrase before reinstalling.
[Slack
thread](https://consensys.slack.com/archives/C8RSKCNCD/p1766165879957189?thread_ts=1764958704.459329&cid=C8RSKCNCD)
### Out of scope for this PR
- Add instrumentation (Sentry or Segment)
- Instrumentation will be added in a different PR:
#39113
- Add new scenarios to storage corruption E2E tests.
- Storage corruption E2E tests are currently disabled (cf. this ticket:
#38080).
- We'll wait until Accounts team enables BIP44 by default in the
codebase (it's currently enabled enabled using a feature flag) before
re-enabling storage corruption E2E tests. Otherwise these tests would be
flaky (cf. this [Slack
thread](https://consensys.slack.com/archives/C01U65ZUS2E/p1767717796349449?thread_ts=1767634209.723779&cid=C01U65ZUS2E)).
[](https://codespaces.new/MetaMask/metamask-extension/pull/39010?quickstart=1)
## **Changelog**
CHANGELOG entry: Fixes Firefox database corruption causing "An
unexpected error occurred" by redirecting to vault recovery when
possible, or displaying guidance to reinstall when recovery is not
possible.
## **Related issues**
Fixes: #10091
Fixes: #35681
## **Manual testing steps**
### Prerequisites
- Firefox browser
- MetaMask installed from Firefox Add-ons store (or local build with
`yarn dist:mv2`)
- The `test-vault-corruption-firefox.sh` script from this
[PR](#38940)
### Test 1: Vault Recovery flow started on storage `get()` failure
1. Install MetaMask and complete onboarding
2. Close Firefox completely
3. Run `./test-vault-corruption-firefox.sh` and select option **5**
(Rename files off-by-one)
4. Open Firefox and click the MetaMask icon
5. **Expected**: You should see the vault recovery screen with "Restore
Accounts" button
6. Click "Restore Accounts", enter your password, complete onboarding
7. **Expected**: Wallet is restored with original accounts
### Test 2: Toast displayed on storage `set()` failure
8. **Expected**: A toast is displayed: "We couldn't save your data -
Back up your Secret Recovery Phrase and reinstall MetaMask if the
problem continues."
9. Click "Back up Secret Recovery Phrase"
10. **Expected**: You are navigated to the SRP reveal page
## **Screenshots/Recordings**
### **Before**
Users see "An unexpected error occurred" with no recovery path.
<img width="426" height="410" alt="Screenshot 2026-01-05 at 06 07 46"
src="https://github.com/user-attachments/assets/a888167c-55ce-4e80-9d83-a53058df06b5"
/>
### **After**
1. **Vault Recovery Flow** (when reads fail):
- Users see the vault recovery screen and can restore their wallet (when
backup exists)
<img width="384" height="578" alt="Screenshot 2026-01-03 at 16 07 30"
src="https://github.com/user-attachments/assets/f64cc4e1-e3ea-4d5a-a635-93cdf687835e"
/>
2. **Storage Error Toast** (when writes fail):
- Toast appears with message: "We couldn't save your data"
- Description: "Back up your Secret Recovery Phrase and reinstall
MetaMask if the problem continues."
- Action button: "Back up Secret Recovery Phrase" → navigates to SRP
reveal
<img width="1521" height="749" alt="Screenshot 2026-01-07 at 23 14 01"
src="https://github.com/user-attachments/assets/e65be4c1-9dd2-492c-b689-13ebf86c6053"
/>
[Loom
recording](https://www.loom.com/share/d51c11812db54a9d8aa29465798bdd33)
## **Pre-merge author checklist**
- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask
Extension Coding
Standards](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [ ] I've included tests if applicable
- [x] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [x] I've applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.
## **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.
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> Introduces recovery UX and error signaling for storage failures, plus
improved error context.
>
> - Persistence: Enhance `PersistenceManager.get()` to handle
`storage.local.get` errors, trigger vault recovery via
`PersistenceError` (now includes `cause`) when an IndexedDB backup
exists; on `set` failures, notify UI via a new `setOnSetFailed` callback
(background wires this to
`AppStateController.setShowStorageErrorToast(true)`).
> - State/Types: Add `appState.showStorageErrorToast` (non-persisted)
and expose in Sentry snapshots and background types.
> - UI: Add `StorageErrorToast` rendered globally in `ToastMaster`
(gated by `selectShowStorageErrorToast`), with action to navigate to SRP
reveal; add i18n strings for title/description/action.
> - Error delivery: In state-corruption handling, include `causeMessage`
from `PersistenceError.cause` when notifying UI.
> - Tests/fixtures: Update unit/e2e/integration snapshots and mocks to
cover new flags, behaviors, and logging.
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
03a318d. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->1 parent c4b0294 commit 0053df5
File tree
18 files changed
+294
-21
lines changed- app
- _locales
- en_GB
- en
- scripts
- constants
- controllers
- lib
- state-corruption
- stores
- test
- e2e
- fixtures
- tests
- metrics/state-snapshots
- settings
- integration/data
- ui/components/app/toast-master
18 files changed
+294
-21
lines changedSome generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
131 | 131 | | |
132 | 132 | | |
133 | 133 | | |
134 | | - | |
| 134 | + | |
135 | 135 | | |
136 | 136 | | |
137 | 137 | | |
| |||
1223 | 1223 | | |
1224 | 1224 | | |
1225 | 1225 | | |
| 1226 | + | |
| 1227 | + | |
| 1228 | + | |
| 1229 | + | |
| 1230 | + | |
1226 | 1231 | | |
1227 | 1232 | | |
1228 | 1233 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
93 | 93 | | |
94 | 94 | | |
95 | 95 | | |
| 96 | + | |
96 | 97 | | |
97 | 98 | | |
98 | 99 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
807 | 807 | | |
808 | 808 | | |
809 | 809 | | |
| 810 | + | |
810 | 811 | | |
811 | 812 | | |
812 | 813 | | |
| |||
900 | 901 | | |
901 | 902 | | |
902 | 903 | | |
| 904 | + | |
903 | 905 | | |
904 | 906 | | |
905 | 907 | | |
| |||
1073 | 1075 | | |
1074 | 1076 | | |
1075 | 1077 | | |
| 1078 | + | |
1076 | 1079 | | |
1077 | 1080 | | |
1078 | 1081 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
166 | 166 | | |
167 | 167 | | |
168 | 168 | | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
169 | 175 | | |
170 | 176 | | |
171 | 177 | | |
| |||
326 | 332 | | |
327 | 333 | | |
328 | 334 | | |
| 335 | + | |
329 | 336 | | |
330 | 337 | | |
331 | 338 | | |
| |||
711 | 718 | | |
712 | 719 | | |
713 | 720 | | |
| 721 | + | |
| 722 | + | |
| 723 | + | |
| 724 | + | |
| 725 | + | |
| 726 | + | |
714 | 727 | | |
715 | 728 | | |
716 | 729 | | |
| |||
938 | 951 | | |
939 | 952 | | |
940 | 953 | | |
| 954 | + | |
| 955 | + | |
| 956 | + | |
| 957 | + | |
| 958 | + | |
| 959 | + | |
| 960 | + | |
| 961 | + | |
| 962 | + | |
| 963 | + | |
| 964 | + | |
| 965 | + | |
941 | 966 | | |
942 | 967 | | |
943 | 968 | | |
| |||
Lines changed: 10 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
43 | 43 | | |
44 | 44 | | |
45 | 45 | | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
46 | 50 | | |
47 | 51 | | |
48 | 52 | | |
| |||
107 | 111 | | |
108 | 112 | | |
109 | 113 | | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
110 | 117 | | |
111 | 118 | | |
112 | 119 | | |
113 | 120 | | |
| 121 | + | |
114 | 122 | | |
115 | 123 | | |
116 | 124 | | |
| |||
172 | 180 | | |
173 | 181 | | |
174 | 182 | | |
175 | | - | |
| 183 | + | |
176 | 184 | | |
177 | 185 | | |
178 | 186 | | |
179 | 187 | | |
180 | 188 | | |
| 189 | + | |
181 | 190 | | |
182 | 191 | | |
183 | 192 | | |
| |||
Lines changed: 28 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3 | 3 | | |
4 | 4 | | |
5 | 5 | | |
6 | | - | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
7 | 11 | | |
8 | 12 | | |
9 | 13 | | |
| |||
102 | 106 | | |
103 | 107 | | |
104 | 108 | | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
105 | 128 | | |
106 | 129 | | |
107 | 130 | | |
| |||
156 | 179 | | |
157 | 180 | | |
158 | 181 | | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
159 | 185 | | |
160 | 186 | | |
161 | 187 | | |
162 | 188 | | |
163 | 189 | | |
164 | 190 | | |
165 | 191 | | |
| 192 | + | |
166 | 193 | | |
167 | 194 | | |
168 | 195 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
21 | 21 | | |
22 | 22 | | |
23 | 23 | | |
| 24 | + | |
24 | 25 | | |
25 | 26 | | |
26 | 27 | | |
| |||
109 | 110 | | |
110 | 111 | | |
111 | 112 | | |
112 | | - | |
| 113 | + | |
113 | 114 | | |
114 | 115 | | |
115 | 116 | | |
116 | 117 | | |
117 | 118 | | |
118 | 119 | | |
119 | | - | |
| 120 | + | |
120 | 121 | | |
121 | 122 | | |
122 | 123 | | |
| |||
0 commit comments