vercel/next.js · error · ImageError

"url" parameter is valid but internal response is invalid

Error message

"url" parameter is valid but internal response is invalid

What it means

Thrown by fetchInternalImage when an internal (relative) image URL is requested via the image optimizer and the mocked request completes but the internal route handler never set a status code (statusCode is falsy/undefined). This indicates the route resolved but the response pipeline did not produce a valid HTTP status, so the optimizer cannot trust the response.

Source

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

  ) => Promise<void>
): Promise<ImageUpstream> {
  try {
    // Coerce HEAD to GET to avoid issues with the image optimizer
    const method = !_req.method || _req.method === 'HEAD' ? 'GET' : _req.method

    const mocked = createRequestResponseMocks({
      url: href,
      method,
      socket: _req.socket,
      maximumResponseBody,
    })

    await handleRequest(mocked.req, mocked.res, parseReqUrl(href))
    await mocked.res.hasStreamed

    if (!mocked.res.statusCode) {
      Log.error('image response failed for', href, mocked.res.statusCode)
      throw new ImageError(
        mocked.res.statusCode,
        '"url" parameter is valid but internal response is invalid'
      )
    }

    if (mocked.res.buffers.length === 0) {
      Log.error('internal image response is empty for', href)
      throw new ImageError(
        400,
        '"url" parameter is valid but internal response is invalid'
      )
    }

    const buffer = Buffer.concat(mocked.res.buffers)
    const contentType = mocked.res.getHeader('Content-Type')
    const cacheControl = mocked.res.getHeader('Cache-Control')
    const etag = extractEtag(mocked.res.getHeader('ETag'), buffer)

View on GitHub (pinned to 0ae8c72462)

Solutions

  1. Check the server logs for the line 'image response failed for <href>' which prints the missing status code, then fix the referenced route to always set a valid HTTP status.
  2. Ensure the internal route the image URL points to actually returns an image (200 with image bytes), not HTML or a redirect.
  3. If the route is dynamic and can fail, add a try/catch in that route to set res.statusCode = 500 on error so the optimizer gets a defined status.
  4. As a workaround, add the `unoptimized` prop to the <Image> to bypass the optimizer for that asset.

Example fix

// before: route handler throws, no status set
export async function GET(req) {
  const data = await generateImage()
  return new Response(data)
}

// after: guarantee a status on failure
export async function GET(req) {
  try {
    const data = await generateImage()
    return new Response(data, { status: 200, headers: { 'Content-Type': 'image/png' } })
  } catch (e) {
    return new Response(null, { status: 500 })
  }
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Before relying on an internal image route, request it directly:
const res = await fetch(`http://localhost:${PORT}${src}`)
if (!res.ok || res.headers.get('content-type')?.startsWith('image/') === false) {
  throw new Error(`Route ${src} does not return a valid image response`)
}

Type guard

function isUsableImageRoute(status?: number, hasBody: boolean): boolean {
  return typeof status === 'number' && status >= 200 && status < 400 && hasBody
}

Try / catch

try {
  return await optimizeInternal(href)
} catch (e) {
  if (e instanceof ImageError && e.message.includes('internal response is invalid')) {
    // fall back to unoptimized or a placeholder
  }
  throw e
}

Prevention

When it happens

Trigger: An <Image src="/some-internal-route.png" /> whose backing route exists but errors before setting res.statusCode, or a dynamic API route that streams nothing and exits. The mocked handler returns but mocked.res.statusCode is undefined.

Common situations: The internal path points at a route guarded by middleware that short-circuits, a route that throws during rendering, or an app-route that returns a Response without a proper status. Misconfigured output:'export' apps or routes missing a default export can also leave no status.

Related errors


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