Skip to content

Log noise: "The destination stream closed early." is a known Next 16.3.0 regression (vercel/next.js#96704) #377

Description

@blove

Every website e2e run logs 37-46 copies of ⨯ Error: The destination stream closed early., all with one digest. All 112 tests pass and no user-visible symptom exists. Filing so nobody re-derives this.

Cause — confirmed upstream and reproduced locally

A known, open Next.js regression: vercel/next.js#96704, reported against 16.3.1-canary.3 / react 19.2.8. Also #96465 (auto-closed for lacking a repro, not on the merits). Two fix PRs open, neither merged: #96715, #96717. No maintainer has commented on either thread.

A community bisection varying only next: 16.2.11 clean, 16.3.0 reproduces, 16.3.1-canary.9 still reproduces. 16.3.0 is where __NEXT_USE_NODE_STREAMS was switched on, routing RSC through pipeNodeReadableToNodeResponse. Verified in our own install:

  • dist/build/define-env.js:92 — hardcoded true, no config opt-out.
  • dist/server/pipe-readable.js:150-153res.once('close', () => readable.destroy()), destroy with no reason.
  • React therefore synthesises Error("The destination stream closed early."), name === "Error".
  • dist/server/pipe-readable.js:32-34isAbortError matches on name only (AbortError / ResponseAborted), so it does not silence it, and create-error-handler.js logs it.

Why a completed response never logs it

From React's source (react-server-dom-turbopack-server.node.development.js): pipe() registers destination.on("close", createCancelHandler(...)) at :6954, and abort() at :4409 is guarded by if (!(11 < request.status)). Successful renders set status = CLOSED (14) before calling destination.end() (:4326). So the later close hits the guard and is a silent no-op.

Every occurrence therefore means a Flight render was still in progress when its socket closed. There is no benign-completion path.

Local proof

A logging reverse proxy in front of next start, correlating socket closes with in-flight renders:

proxy behaviour mid-stream client disconnects errors suite
pass through (destroy upstream) 111 40 112/112
absorb after headers 67 absorbed, 42 destroyed pre-headers 13 112/112
absorb fully (drain, never destroy) 103 absorbed 0 112/112

Driving the early close to zero drives the error to zero. The aborted requests are overwhelmingly ?_rsc= prefetches of /docs/**<Link> prefetches from the docs sidebar's 41 links, cancelled when Playwright tears a page down. /docs/[[...slug]] is the only request-time Flight render in a production build; everything else is prerendered.

Notably favicon.ico is a prerendered route handler, not a Flight render — the favicon test is implicated only by when it ends (immediately after waitForDocsReady, at the peak of the prefetch burst), not by what it fetches.

What we should do

Nothing, for now. There is no supported suppression: onRequestError is the only hook and we do not export one; the console line comes from Next's own logging with no documented filter. Pinning to 16.2.x would trade real fixes for log tidiness.

Watch vercel/next.js#96704 and drop this issue when it lands.

Related

#376 halves docs render time by removing a duplicate MDX+shiki compile. That narrows the window but does not fix this (34 lines after vs 37-46 before — noise).

Dead ends, so nobody repeats them

Each produced zero: complete HTML loads; complete RSC loads; curl aborts on both; Node socket-destroy on first byte across five route/mode combinations; real Chromium closing the context at 0/50/150/400/1200ms; cold vs warm server; hydration-then-teardown; prefetches allowed to settle; the query-stringed favicon URL repeated; request.get before page.goto. Patching Next's compiled runtime to log the request URL breaks itdestination is not bound at all three occurrences of the string; 30 tests fail. Use an external proxy instead.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions