{"record":{"id":"6aca2d5b57c3e965","repo":"withastro/astro","slug":"incorrect-transform-returned-by-parseurl","errorCode":null,"errorMessage":"Incorrect transform returned by `parseURL`","messagePattern":"Incorrect transform returned by `parseURL`","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/astro/src/assets/endpoint/generic.ts","lineNumber":26,"sourceCode":"import { etag } from '../utils/etag.js';\nimport { loadImage } from './loadImage.js';\n\n/**\n * Endpoint used in dev and SSR to serve optimized images by the base image services\n */\nexport const GET: APIRoute = async ({ request, logger }) => {\n\ttry {\n\t\tconst imageService = await getConfiguredImageService();\n\n\t\tif (!('transform' in imageService)) {\n\t\t\tthrow new Error('Configured image service is not a local service');\n\t\t}\n\n\t\tconst url = new URL(request.url);\n\t\tconst transform = await imageService.parseURL(url, imageConfig, logger);\n\n\t\tif (!transform?.src) {\n\t\t\tthrow new Error('Incorrect transform returned by `parseURL`');\n\t\t}\n\n\t\tlet inputBuffer: ArrayBuffer | undefined = undefined;\n\n\t\tconst isRemoteImage = isRemotePath(transform.src);\n\n\t\tif (isRemoteImage && isRemoteAllowed(transform.src, imageConfig) === false) {\n\t\t\treturn new Response('Forbidden', { status: 403 });\n\t\t}\n\n\t\tconst sourceUrl = new URL(transform.src, url.origin);\n\n\t\t// Have we been tricked into thinking this is local?\n\t\tif (!isRemoteImage && sourceUrl.origin !== url.origin) {\n\t\t\treturn new Response('Forbidden', { status: 403 });\n\t\t}\n\n\t\tinputBuffer = await loadImage(","sourceCodeStart":8,"sourceCodeEnd":44,"githubUrl":"https://github.com/withastro/astro/blob/e294953aa8aadd98d5be92e60a03037b05dbdfd4/packages/astro/src/assets/endpoint/generic.ts#L8-L44","documentation":"Thrown by the dev/SSR `/_image` endpoint (packages/astro/src/assets/endpoint/generic.ts:26) when the configured service's `parseURL(url, imageConfig)` returns undefined/null or an object without a usable `src`. `parseURL` is what decodes the endpoint's query string (href, w, h, f, q) into a transform, so a URL the service cannot decode produces this error; it is caught and turned into a 500 response.","triggerScenarios":"Requesting `/_image` without the params the built-in services require (e.g. missing `?href=...`), or a custom image service whose `parseURL` implementation returns a malformed object (no `src`) for URLs its own `getUrl` generated.","commonSituations":"Hand-crafted or truncated `/_image` URLs; rewriting/proxying layers stripping query params; upgrading Astro versions where the endpoint's query param names changed while a custom service still parsed the old format.","solutions":["Use the URLs produced by `getImage()` / `<Image>` to reach `/_image` — they always include every param the built-in services' `parseURL` requires.","If calling `/_image` manually, include the required params, e.g. `/_image?href=%2Fassets%2Fimg.png&w=800&f=webp`.","Custom service authors: make `parseURL()` return an object with a non-empty `src` for every URL shape your `getUrl()` can emit, and unit-test the getUrl/parseURL round-trip."],"exampleFix":"// before — hand-written, missing the href param\nconst src = '/_image?w=800&f=webp';\n\n// after — let getImage build the URL\nimport { getImage } from 'astro:assets';\nconst src = (await getImage({ src: '/assets/img.png', width: 800, format: 'webp' })).src;","handlingStrategy":"validation","validationCode":"// before proxying/rewriting any /_image request, keep the required params intact\nconst u = new URL(request.url);\nif (!u.searchParams.has('href')) {\n  return new Response('Missing href param', { status: 400 });\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never hand-write /_image URLs; always take them from getImage()/<Image> output.","Custom service authors: unit-test that parseURL(getUrl(transform)) round-trips back to the transform.","When proxying dev traffic, preserve the full query string."],"tags":["images","image-endpoint","custom-service","dev-server"],"backgroundTag":"invalid-image-request-url","analyzedSha":"e294953aa8aadd98d5be92e60a03037b05dbdfd4","analyzedAt":"2026-09-09T03:35:40.650Z","contentChangedAt":"2026-09-09T03:35:40.650Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}