Commit f638960
authored
fix(premature close): gracefully end extension stream muxes to prevent "Premature close" errors (#37400)
## **Description**
This PR is an AI-generated fix aiming to fix the #1 error in Sentry
(3.8M occurrences/month) by adding graceful shutdown handlers to
extension context streams, preventing "ERR_STREAM_PREMATURE_CLOSE"
errors.
The error was initially discussed in this [Slack
thread](https://consensys.slack.com/archives/CTQAGKY5V/p1761904147325279?thread_ts=1761646886.397379&cid=CTQAGKY5V).
#### Disclaimer
Since we haven't found a reliable way to manually reproduce these
"Premature close" errors, there's no "before/after proof" that this PR
fixes it.
### Problem
When underlying transport streams (e.g., `WindowPostMessageStream`,
`ExtensionPortStream`) close or end abruptly—during page navigation, tab
closure, or extension disconnection—the `ObjectMultiplex` instances in
**extension context** do not gracefully shut down. This results in
"Premature close" errors being thrown by the stream pipeline.
### Solution
Added graceful shutdown handlers to **extension context** streams that:
1. **Listen for transport termination**: Attach `once('close')` and
`once('end')` listeners to underlying transport streams
2. **Proactively close muxes**: When transport terminates, check if mux
is still open (`!destroyed && !writableEnded`) and call `mux.end()`
before pipeline error detection kicks in
3. **Prevent error propagation**: By closing gracefully, we avoid
"ERR_STREAM_PREMATURE_CLOSE" errors in the pipeline
### Key Design Decision: Page Context vs Extension Context
**Why handlers are ONLY in extension context streams:**
| Context | Files | Needs Handlers? | Reason |
|---------|-------|-----------------|--------|
| **Page Context** | `inpage.js` | ❌ **NO** | Browser automatically
destroys the entire script execution context on navigation. Adding
handlers can actually CAUSE disconnection errors during rapid
navigation. |
| **Extension Context** |
`provider-stream.ts`<br>`phishing-stream.ts`<br>`cookie-handler-stream.ts`
| ✅ **YES** | Extension context persists across page navigations. Muxes
remain active when transports disconnect, causing "Premature close"
errors without explicit cleanup. |
This approach mirrors the solution in
[#33470](#33470) for
CAIP streams.
[](https://codespaces.new/MetaMask/metamask-extension/pull/37400?quickstart=1)
## **Changelog**
CHANGELOG entry: Fixed "Premature close" stream errors in extension
context by adding graceful shutdown handlers
## **Related issues**
Fixes: #26337
Fixes: #35241
Fixes: #21078
## **Manual testing steps**
(disclaimer: the error is hard to reproduce, even with these manual
testing steps)
1. Open a dapp page connected to MetaMask (e.g.,
https://metamask.github.io/test-dapp/)
2. Rapidly navigate between pages multiple times or close/reopen tabs
3. Open DevTools Console and check for errors
4. Open Extension Background Console: `chrome://extensions` → MetaMask →
"service worker"
5. Verify no "Premature close" errors appear during page navigation
6. Test deep link navigation: Navigate to
`chrome-extension://{extension-id}/home.html#link?u=/buy` and verify it
redirects cleanly without errors
**Expected**: Clean shutdowns with no "Premature close" errors in
console
## **Screenshots/Recordings**
### **Before**
- "Premature close" errors appear in extension background console during
page navigation
- Sentry reports 3.8M occurrences per month
### **After**
- Extension context streams shut down gracefully
- No "Premature close" errors during normal operations
- E2E tests pass (including deep-link tests that navigate rapidly)
## **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
- [x] 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]
> Add graceful shutdown handlers to extension-context multiplexers for
provider, phishing, and cookie-handler streams, plus tests; clarify no
handlers in page-context in inpage.
>
> - **Streams (extension context)**:
> - **Graceful shutdown handlers**: End muxes on underlying transport
`close`/`end` to avoid `ERR_STREAM_PREMATURE_CLOSE` in:
> - `app/scripts/streams/provider-stream.ts` (`pageMux`, `extensionMux`)
> - `app/scripts/streams/phishing-stream.ts` (`phishingPageMux`,
`phishingExtMux`)
> - `app/scripts/streams/cookie-handler-stream.ts`
(`cookieHandlerPageMux`, `cookieHandlerMux`)
> - **Logging/notifications**: Preserve existing pipeline callbacks;
continue logging via `logStreamDisconnectWarning` and sending
`METAMASK_STREAM_FAILURE` where applicable.
> - **Inpage (page context)**:
> - `app/scripts/inpage.js`: Add documentation explaining why graceful
shutdown handlers are not added in page context.
> - **Tests**:
> - Add filter tests to assert unconditional logging on pipeline
completion/error:
> - `provider-stream.filter.test.ts`
> - `phishing-stream.filter.test.ts`
> - `cookie-handler-stream.filter.test.ts`
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
e72ec5e. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->1 parent 709e4d7 commit f638960
File tree
7 files changed
+593
-0
lines changed- app/scripts
- streams
7 files changed
+593
-0
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
67 | 67 | | |
68 | 68 | | |
69 | 69 | | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
70 | 102 | | |
71 | 103 | | |
72 | 104 | | |
| |||
Lines changed: 134 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
45 | 45 | | |
46 | 46 | | |
47 | 47 | | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
48 | 74 | | |
49 | 75 | | |
50 | 76 | | |
| |||
80 | 106 | | |
81 | 107 | | |
82 | 108 | | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
83 | 124 | | |
84 | 125 | | |
85 | 126 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
0 commit comments