AI & Agent Dev Bug Sandbox logo
AI & Agent Dev Bug Sandbox
Back to Radar

Next/Image: A Client That Disconnects Mid-Request Permanently Hangs That /_Next/Image Cache Key For Every Later Client (Self-Hosted)

A client disconnecting during /_next/image optimization causes the response generator promise to never settle, permanently poisoning the cache key for all subsequent requests on self-hosted Next.js (next start/standalone).

highConfidence 95%Next.jsAffected V16.2.6Affected V16.4.0-Canary.22

Origin Analysis

MockedResponse is constructed with the client's socket; when the client disconnects, send sees socket.writable=false and treats the response as already finished without calling end(), so hasStreamed never resolves, leaving the pending promise in Batcher for that cache key forever.
1. Clone https://github.com/lpoulter-gradle/next-image-abort-hang-repro 2. Run pnpm install && pnpm run gen && pnpm run build && pnpm start 3. In another terminal run pnpm run repro 4. Observe follow-up requests for aborted widths timeout after ~8s; control request (never aborted) succeeds; subsequent curl for same URL hangs indefinitely until server restart.

Fixing Code Block

Edge Case Audit

This change assumes no internal logic in the image optimizer pipeline relies on res.socket being the client connection. Tests should cover normal optimization, aborted requests, and concurrent requests for the same key. If any regression appears (e.g., code that expects res.socket for connection info), revert this patch and instead implement a timeout on the responseGenerator in ResponseCache to evict dead pending promises. The socket:null change alone does not add a safety timeout; a belt-and-braces timeout in ResponseCache is still recommended for long-running self-hosted processes.

Ecosystem Topology