{"record":{"id":"da6e7957400cd38b","repo":"vercel/next.js","slug":"url-parameter-is-valid-but-internal-response-is","errorCode":null,"errorMessage":"\"url\" parameter is valid but internal response is invalid","messagePattern":"\"url\" parameter is valid but internal response is invalid","errorType":"http","errorClass":"ImageError","httpStatus":null,"severity":"error","filePath":"packages/next/src/server/image-optimizer.ts","lineNumber":994,"sourceCode":"  ) => Promise<void>\n): Promise<ImageUpstream> {\n  try {\n    // Coerce HEAD to GET to avoid issues with the image optimizer\n    const method = !_req.method || _req.method === 'HEAD' ? 'GET' : _req.method\n\n    const mocked = createRequestResponseMocks({\n      url: href,\n      method,\n      socket: _req.socket,\n      maximumResponseBody,\n    })\n\n    await handleRequest(mocked.req, mocked.res, parseReqUrl(href))\n    await mocked.res.hasStreamed\n\n    if (!mocked.res.statusCode) {\n      Log.error('image response failed for', href, mocked.res.statusCode)\n      throw new ImageError(\n        mocked.res.statusCode,\n        '\"url\" parameter is valid but internal response is invalid'\n      )\n    }\n\n    if (mocked.res.buffers.length === 0) {\n      Log.error('internal image response is empty for', href)\n      throw new ImageError(\n        400,\n        '\"url\" parameter is valid but internal response is invalid'\n      )\n    }\n\n    const buffer = Buffer.concat(mocked.res.buffers)\n    const contentType = mocked.res.getHeader('Content-Type')\n    const cacheControl = mocked.res.getHeader('Cache-Control')\n    const etag = extractEtag(mocked.res.getHeader('ETag'), buffer)\n","sourceCodeStart":976,"sourceCodeEnd":1012,"githubUrl":"https://github.com/vercel/next.js/blob/0ae8c72462952df163f1b1e0726641bc5b40dc93/packages/next/src/server/image-optimizer.ts#L976-L1012","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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.","Ensure the internal route the image URL points to actually returns an image (200 with image bytes), not HTML or a redirect.","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.","As a workaround, add the `unoptimized` prop to the <Image> to bypass the optimizer for that asset."],"exampleFix":"// before: route handler throws, no status set\nexport async function GET(req) {\n  const data = await generateImage()\n  return new Response(data)\n}\n\n// after: guarantee a status on failure\nexport async function GET(req) {\n  try {\n    const data = await generateImage()\n    return new Response(data, { status: 200, headers: { 'Content-Type': 'image/png' } })\n  } catch (e) {\n    return new Response(null, { status: 500 })\n  }\n}","handlingStrategy":"try-catch","validationCode":"// Before relying on an internal image route, request it directly:\nconst res = await fetch(`http://localhost:${PORT}${src}`)\nif (!res.ok || res.headers.get('content-type')?.startsWith('image/') === false) {\n  throw new Error(`Route ${src} does not return a valid image response`)\n}","typeGuard":"function isUsableImageRoute(status?: number, hasBody: boolean): boolean {\n  return typeof status === 'number' && status >= 200 && status < 400 && hasBody\n}","tryCatchPattern":"try {\n  return await optimizeInternal(href)\n} catch (e) {\n  if (e instanceof ImageError && e.message.includes('internal response is invalid')) {\n    // fall back to unoptimized or a placeholder\n  }\n  throw e\n}","preventionTips":["Always set a valid HTTP status in any route that serves images.","Add the `unoptimized` prop for routes you don't fully control.","Smoke-test internal image URLs with curl before wiring them to next/image."],"tags":["image-optimizer","internal-route","response"],"analyzedSha":"0ae8c72462952df163f1b1e0726641bc5b40dc93","analyzedAt":"2026-08-06T19:44:29.143Z","schemaVersion":2},"datasetVersion":"2026-08-07T02:17:10.218Z"}