experimental.testProxy hangs raw TCP sockets (regression in 16.3.0, works in 16.2.12)
### Link to the code that reproduces this issue
https://github.com/antoinerousseau/next-testproxy-repro
### To Reproduce
```bash
git clone <gist above into a folder> repro && cd repro
pnpm install
node echo-server.js & # plain TCP echo server on :3901
ENABLE_TEST_PROXY=true pnpm build
ENABLE_TEST_PROXY=true ECHO_PORT=3901 pnpm start # next start -p 3900
curl http://localhost:3900/api/probe # hangs forever
```
The route (`app/api/probe/route.js`) does nothing but open a raw
`net.Socket` (no `fetch`, no HTTP) to a plain TCP echo server, write a byte,
and await the echo:
```js
import net from "node:net";
function probeRawSocket(port) {
return new Promise((resolve, reject) => {
const socket = net.createConnection({ host: "127.0.0.1", port }, () => {
socket.write("ping");
});
socket.on("data", (data) => {
socket.end();
resolve(data.toString());
});
socket.on("error", reject);
});
}
export async function GET() {
const start = Date.now();
const reply = await probeRawSocket(Number(process.env.ECHO_PORT));
return Response.json({ reply, ms: Date.now() - start });
}
```
`next.config.js` only sets:
```js
module.exports = {
experimental: {
testProxy: process.env.ENABLE_TEST_PROXY === "true",
},
};
```
### Current vs. Expected behavior
With `experimental.testProxy: true` on **Next.js 16.3.0**, the request to
`/api/probe` never resolves — no response, no error, no timeout. Confirmed
via `lsof` on the `next-server` process that the outbound TCP connection to
the echo server is never even opened at the socket level.
With the exact same code and env, only changing the version:
| next version | testProxy | result |
|---|---|---|
| 16.2.12 | true | resolves in ~6ms |
| 16.3.0 | true | **hangs forever** |
| 16.3.0 | false | resolves in ~3ms |
Expected: `testProxy` should only intercept `fetch()`/HTTP traffic (per its
documented purpose for `next/experimental/testmode`), and should not affect
unrelated raw `net.Socket` connections — matching the 16.2.12 behavior.
### Real-world impact
We hit this via `next/experimental/testmode/playwright/msw`, which flips this
same flag on to let Playwright/MSW intercept `fetch()` calls in e2e tests.
One of our API routes also runs a normal `pg` (`node-postgres`) query in the
same request. After upgrading 16.2.12 → 16.3.0, every Playwright test whose
setup hits that route now hangs until Playwright's default 30s hook timeout,
and with retries + a single CI worker this silently burns ~90 minutes before
the job reports failure — with no actual error surfaced anywhere. We
confirmed with `pg_stat_activity`/`lsof` that Postgres never receives a
connection attempt while stuck.
This minimal repro shows it's not `pg`-specific — any raw socket traffic
appears to be swallowed by `testProxy` in 16.3.0 rather than passed through.
### Provide environment information
```
Operating System:
Platform: darwin
Arch: arm64
Version: Darwin Kernel Version 25.5.0
Node: v24.18.0
Next.js: 16.3.0 (regression; 16.2.12 unaffected)
```
Also reproduced in CI on `ubuntu-24.04` (GitHub Actions), same result.
### Which area(s) are affected?
Not sure / Other (experimental.testProxy / next/experimental/testmode)
### Additional context
This was discovered while upgrading an app from 16.2.12 to 16.3.0. Reverting
only the `next`/`@next/env` version (keeping `experimental.testProxy: true`)
resolves it, confirming it's a version regression and not a config issue on
our side.
---
(Previously filed as #96766, auto-closed for linking a gist instead of a repo — refiling with a proper public repo link. Also likely related to #96521, which pins the root cause more precisely: the `@mswjs/interceptors` bump to socket-level interception in #96059.)
2 条评论