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
- Open the image URL directly in a browser and confirm it serves actual image bytes, not an HTML/JSON page.
- Fix the source URL to point at a real image asset (check for 404/redirect/login walls).
- Ensure the upstream sets a correct Content-Type and returns the full file without truncation.
- 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
- Verify image URLs resolve to actual image bytes before using next/image.
- Watch for soft-404 HTML pages returned with 200 status.
- Validate Content-Type headers from upstream sources.
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
- "url" parameter is valid but internal response is invalid
- "url" parameter is valid but image type is not allowed
- Unable to optimize image and unable to fallback to upstream
AI-assisted analysis of vercel/next.js@0ae8c72462 (2026-08-06).
Data as JSON: /api/errors/a2326eeb3a6f286c.
Report an issue: GitHub.