{"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 }) => {\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);\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/d081033d5fe8e8a68c4bbbad4af9d2deb9c74bca/packages/astro/src/assets/endpoint/generic.ts#L8-L44","documentation":"After imageService.parseURL(url, imageConfig), the endpoint checks the returned transform object has a src. If parseURL returns null/undefined or an object without src, the optimization URL could not be parsed into a valid transform, and a plain Error is thrown.","triggerScenarios":"A malformed image-optimization URL (missing the href/query parameters the service expects), or a custom local service whose parseURL does not extract/return src.","commonSituations":"Hand-crafting or manually editing the _image URL; a custom service with a buggy parseURL; stale URLs after changing the service.","solutions":["Request optimized images through <Image>/<Picture> or getImage() so URLs are well-formed.","If you ship a custom local service, make parseURL return { src, width, height, format, ... }.","Do not hand-build optimization endpoint URLs."],"exampleFix":"// before - hand-crafted request\nfetch('/_image?href=');\n// after - go through the component so the URL is built correctly\nimport { Image } from 'astro:assets';\n<Image src={myImg} widths={[240, 540, 720]} sizes=\"...\" />","handlingStrategy":"validation","validationCode":"// Do not hand-build optimization URLs; use getImage()/Image so parseURL always returns src.\nimport { getImage } from 'astro:assets';\nconst img = await getImage({ src: mySrc, width: 540, format: 'webp' });\n// img.src is a valid optimization URL.","typeGuard":"function isValidTransform(t: any): t is { src: string } {\n  return t && typeof t === 'object' && typeof t.src === 'string' && t.src.length > 0;\n}","tryCatchPattern":"try {\n  await getConfiguredImageService().then((s) => s.parseURL?.(new URL(req.url), imageConfig));\n} catch (e) {\n  if (e instanceof Error && /parseURL/i.test(e.message)) return new Response('Bad image URL', { status: 400 });\n  throw e;\n}","preventionTips":["Always request optimized images via <Image>/<Picture>/getImage().","If shipping a custom service, return { src, ... } from parseURL.","Never construct _image URLs by hand."],"tags":["image","url-parsing","image-service"],"backgroundTag":null,"analyzedSha":"d081033d5fe8e8a68c4bbbad4af9d2deb9c74bca","analyzedAt":"2026-08-12T13:37:29.035Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}