Skip to content

Instantly share code, notes, and snippets.

@antoinerousseau
Created August 5, 2026 21:38
Show Gist options
  • Select an option

  • Save antoinerousseau/f1d23dec48cec1e8ba504776532dfeb2 to your computer and use it in GitHub Desktop.

Select an option

Save antoinerousseau/f1d23dec48cec1e8ba504776532dfeb2 to your computer and use it in GitHub Desktop.
Minimal repro: Next.js 16.3.0 experimental.testProxy hangs raw TCP sockets

experimental.testProxy hangs raw TCP sockets (16.3.0 regression)

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 on 16.3.0

Set ENABLE_TEST_PROXY=false (or install next@16.2.12) and the same request resolves in a few ms.

Files

  • next.config.jsexperimental.testProxy gated by ENABLE_TEST_PROXY.
  • app/api/probe/route.js — opens a raw net.Socket (no fetch, no HTTP) to the echo server, writes a byte, awaits the echo, returns it.
  • echo-server.js — trivial TCP echo server standing in for any non-fetch TCP consumer (Postgres client, Redis client, etc).

What we observed

  • testProxy: true + next@16.2.12: request resolves in ~6ms.
  • testProxy: true + next@16.3.0: request never resolves (verified no timeout, no error — just hangs indefinitely).
  • testProxy: false + next@16.3.0: request resolves in ~3ms.

Same Node version, same code, only the next version / flag differs.

Real-world impact

testProxy is the flag next/experimental/testmode/playwright/msw turns on to intercept fetch() for MSW-based mocking in Playwright e2e tests. In a real app we use it that way, and one of our API routes also does a normal pg (node-postgres) query in the same request. On 16.3.0, any request that reaches a pg connection while testProxy is enabled hangs forever — no TCP connection to Postgres is ever attempted (confirmed via pg_stat_activity and lsof on the server process). Every Playwright test whose fixture calls that route times out after Playwright's default 30s hook timeout, and with retries + a single worker this silently burns ~90 minutes in CI before the job reports failure.

This repro shows the same hang for TCP sockets in general (not pg-specific), so it looks like testProxy in 16.3.0 started intercepting/blocking non-fetch socket traffic it doesn't know how to proxy, and never falls through.

// Plain TCP echo server, unrelated to Next.js — stands in for any
// non-fetch/non-HTTP TCP consumer (a Postgres driver, Redis client, etc).
const net = require("node:net");
const server = net.createServer((socket) => {
socket.on("data", (data) => socket.write(data));
});
server.listen(process.env.ECHO_PORT || 3901, () => {
console.log("echo server up on", server.address().port);
});
export default function RootLayout({ children }) {
return (
<html>
<body>{children}</body>
</html>
);
}
/** @type {import('next').NextConfig} */
module.exports = {
experimental: {
// Toggle this to reproduce: true hangs, false/omitted works.
testProxy: process.env.ENABLE_TEST_PROXY === "true",
},
};
{
"name": "next-testproxy-repro",
"version": "0.0.0",
"private": true,
"scripts": {
"build": "next build",
"start": "next start -p 3900"
},
"dependencies": {
"next": "16.3.0",
"react": "19.2.8",
"react-dom": "19.2.8"
}
}
export default function Page() {
return <div>ok</div>;
}
import net from "node:net";
// Opens a raw TCP connection (no fetch/HTTP involved) to a plain TCP echo
// server, writes a byte, and waits for the echo back.
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 });
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment