cloudflare / cloudflare/workers-sdk
Miniflare Browser Rendering leaves all but the first Chrome orphaned on direct SIGINT
- Dominant language
- TypeScript
- Stars
- 4.5k
- Forks
- 1.5k
- Avg merge
- 3d 8h
- Merged PRs (30d)
- 187
Description
### What versions & operating system are you using?
```text
System:
OS: macOS 26.6.1 (25G76)
CPU: Apple Silicon (arm64)
Binaries:
Node: 26.7.0
pnpm: 11.11.0
Packages:
wrangler: 4.127.1
miniflare: 5.20260828.0-alpha
workerd: 1.20260828.1
@cloudflare/playwright: 1.3.0
```
I also originally observed the same behaviour with Wrangler 4.126.0 / Miniflare 5.20260825.0-alpha.
### Please provide a link to a minimal reproduction
https://gist.github.com/banchichen/e6ce9f82111ab32195b2af11d819d413
### Describe the Bug
When a local Browser Rendering worker has more than one Chrome session, sending `SIGINT` directly to the Wrangler Node process kills Wrangler and the first Chrome process tree, but leaves the remaining Chrome process tree orphaned.
Steps to reproduce:
1. Install and run the linked worker with `pnpm install && pnpm dev`.
2. Call it twice, at least one second apart, to acquire two Browser Rendering sessions.
3. Find the Wrangler Node process (the process running `wrangler-dist/cli.js dev`, not its `pnpm` parent).
4. Run `kill -INT `.
5. Inspect processes whose command contains `miniflare-.../browser-rendering`.
Before `SIGINT`, the two Chrome root processes in my Wrangler 4.127.1 run were:
```text
90046 89899 Ss .../Google Chrome for Testing .../browser-rendering/profile-42d0d8ed-...-0
90074 89899 Ss .../Google Chrome for Testing .../browser-rendering/profile-d85db5ca-...-0
```
Five seconds after `kill -INT 89899`, Wrangler and PID 90046 had exited, but the second Chrome was still alive and reparented to PID 1:
```text
90074 1 Ss .../Google Chrome for Testing .../browser-rendering/profile-d85db5ca-...-0
```
The same result was deterministic across repeated runs. Sending Ctrl-C through a package-manager PTY can take a different, graceful shutdown path, so the direct signal to the Wrangler Node process is important to the reproduction.
Expected behaviour: all Browser Rendering Chrome process trees exit when Wrangler receives `SIGINT`.
This appears to be a remaining edge case after the teardown work in #14727 / commit 3f3afbbf136c404d26ee39d187a44adb06c1b6e8. That change successfully cleans up normal sessions when Miniflare's centralized disposal completes, but direct `SIGINT` can bypass cleanup for all but the first session.
The likely cause is the interaction with `@puppeteer/browsers` signal handling:
- Browser Rendering currently passes `handleSIGTERM: false`, but leaves `handleSIGINT` at its default `true`: https://github.com/cloudflare/workers-sdk/blob/f8085545bcaa2c639f171c25e4424685036a0e10/packages/miniflare/src/plugins/browser-rendering/index.ts#L229-L232
- `@puppeteer/browsers` registers one `SIGINT` handler per launched `Process`. Its shared dispatcher iterates those handlers, but each handler kills its own browser and immediately calls `process.exit(130)`.
- Therefore the first browser handler exits the Wrangler process before later browser handlers can run, and before the centralized multi-browser teardown can complete.
One possible direction would be to disable per-browser `SIGINT` handling (and let Wrangler/Miniflare own shutdown for the full tracked set), plus add a multi-session direct-SIGINT regression test.
### Please provide any relevant error logs
Wrangler exits with status 130 as expected. There is no application error; the observable failure is the remaining Chrome process with `PPID 1`.
Contributor guide
Research direction
Start in packages/miniflare/src/plugins/browser-rendering/index.ts at the handleSIGTERM/handleSIGINT options, then run the linked gist with two sessions and send SIGINT directly to the Wrangler Node process. Add a multi-session direct-SIGINT regression test and verify that all browser-rendering Chrome trees exit while Wrangler returns status 130.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- node.js, playwright, typescript
- Domain
- cli
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 68/100