vercel/next.js · error · ImageError

The requested resource isn't a valid image.

Error message

The requested resource isn't a valid image.

What it means

Thrown by imageOptimizer after the upstream/internal image bytes are fetched: detectContentType returned null, a non-'image/' MIME, or a comma-containing type. The content at the URL is not a recognizable image, so optimization cannot proceed.

Source

Thrown at packages/next/src/server/image-optimizer.ts:1095

    getMaxAge(imageUpstream.cacheControl)
  )

  const upstreamType = await detectContentType(upstreamBuffer)

  if (
    !upstreamType ||
    !upstreamType.startsWith('image/') ||
    upstreamType.includes(',')
  ) {
    if (!opts.silent) {
      Log.error(
        "The requested resource isn't a valid image for",
        href,
        'received',
        upstreamType
      )
    }
    throw new ImageError(400, "The requested resource isn't a valid image.")
  }
  if (
    upstreamType.startsWith('image/svg') &&
    !nextConfig.images.dangerouslyAllowSVG
  ) {
    if (!opts.silent) {
      Log.error(
        `The requested resource "${href}" has type "${upstreamType}" but dangerouslyAllowSVG is disabled. Consider adding the "unoptimized" property to the <Image>.`
      )
    }
    throw new ImageError(
      400,
      '"url" parameter is valid but image type is not allowed'
    )
  }
  if (ANIMATABLE_TYPES.includes(upstreamType) && isAnimated(upstreamBuffer)) {
    if (!opts.silent) {
      Log.warnOnce(

View on GitHub (pinned to 0ae8c72462)

Solutions

  1. Open the image URL directly in a browser and confirm it serves actual image bytes, not an HTML/JSON page.
  2. Fix the source URL to point at a real image asset (check for 404/redirect/login walls).
  3. Ensure the upstream sets a correct Content-Type and returns the full file without truncation.
  4. If serving SVG, note this specific error path rejects SVG only when not detected as image; see dangerouslyAllowSVG for SVG handling.

Example fix

// before: URL returns an HTML 404 page with 200 status
<Image src="https://example.com/missing.png" />

// after: point to a real image asset
<Image src="https://example.com/real-photo.png" />
Defensive patterns

Strategy: type-guard

Validate before calling

const res = await fetch(url)
const type = res.headers.get('content-type') || ''
if (!type.startsWith('image/')) throw new Error(`${url} is not an image (got ${type})`)

Type guard

import { fileTypeFromBuffer } from 'file-type'
async function isImage(buf: Buffer): Promise<boolean> {
  const t = await fileTypeFromBuffer(buf)
  return !!t && t.mime.startsWith('image/')
}

Try / catch

try {
  return <Image src={url} ... />
} catch (e) {
  if (e.message.includes("isn't a valid image")) return <FallbackImg />
  throw e
}

Prevention

When it happens

Trigger: The fetched bytes are HTML (e.g. a 404 page), JSON, a text error page, or a corrupted/truncated image whose magic bytes don't match any known image format. detectContentType sniffs the byte signature.

Common situations: The image URL returns an HTML error page with a 200 status (soft 404), the upstream returns a login redirect page, the file extension lies about content, or the image is truncated mid-download.

Related errors


AI-assisted analysis of vercel/next.js@0ae8c72462 (2026-08-06). Data as JSON: /api/errors/a2326eeb3a6f286c. Report an issue: GitHub.