vercel / vercel/next.js

next/image: /_next/image returns 500 (raw "fetch failed") when the remote host fails DNS or refuses the connection

Open Beginner friendly
#98,569 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
JavaScript
Stars
142k
Forks
32.5k
Avg merge
2d 14h
Merged PRs (30d)
351

Description

Link to the code that reproduces this issue

https://github.com/matias-berger-atlas/next-image-enotfound-500-repro

To Reproduce
  1. Clone the repro (next@16.3.4 pinned), npm install
  2. next.config.ts allows does-not-resolve.example.invalid in images.remotePatterns; app/page.tsx renders one <Image> from that host
  3. npm run build && npm start
  4. curl -i 'http://localhost:3000/_next/image?url=https%3A%2F%2Fdoes-not-resolve.example.invalid%2Fa.jpg&w=640&q=75'
Current vs. Expected behavior

Current: 500 Internal Server Error, and the server logs

⨯ [TypeError: fetch failed]
  [cause]: Error: getaddrinfo ENOTFOUND does-not-resolve.example.invalid

Same for ECONNREFUSED and TLS handshake errors: any rejection of the upstream fetch().

Expected: a client-visible upstream error status (502 or 404) like every other upstream failure in the optimizer already produces. In fetchExternalImage:

const res = await fetch(href, { signal: AbortSignal.timeout(7000), redirect: 'manual' }).catch((err) => err)
if (res instanceof Error) {
  const err = res as Error
  if (err.name === 'TimeoutError') {
    throw new ImageError(504, '"url" parameter is valid but upstream response timed out')
  }
  throw err   // <-- raw TypeError('fetch failed') escapes; the route has no status for it => 500
}

Upstream timeout -> 504. Upstream non-2xx -> ImageError(res.status). Upstream too large -> 413. Only a rejected fetch (DNS failure, connection refused, TLS error) is rethrown raw and becomes a 500 with a stack trace in the logs. Still the case on 16.4.0-canary.26.

Why it matters: a third-party image CDN going dark (a real case for us: mediavault.point2.com stopped resolving, ~21k images) turns into a flood of 500s on the app's own ingress and alerts, for something that is entirely the upstream's fault and that the client <img onError> already handles fine when given a 4xx/5xx.

Suggested fix (we run this as a pnpm patch today):

throw new ImageError(502, '"url" parameter is valid but upstream request failed')
Provide environment information

Next.js 16.3.4 (also reproduced on 16.3.0 and 16.4.0-canary.26), Node 22, Linux (standalone, self-hosted), and locally on macOS.

Which area(s) are affected?

Image (next/image)

Which stage(s) are affected?

next start (local), Other (self-hosted / standalone)

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the linked reproduction, using next.config.ts, app/page.tsx, and the documented npm install, npm run build && npm start, and curl steps. Then inspect fetchExternalImage and its handling of rejected fetches. Done means DNS, connection-refused, and TLS failures return a client-visible upstream error such as 502 instead of an unhandled 500.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, nextjs, node.js
Domain
backend, web-dev
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
78/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.