withastro/astro · error · Error
Incorrect transform returned by `parseURL`
Error message
Incorrect transform returned by `parseURL`
What it means
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.
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.
Example fix
// before — hand-written, missing the href param
const src = '/_image?w=800&f=webp';
// after — let getImage build the URL
import { getImage } from 'astro:assets';
const src = (await getImage({ src: '/assets/img.png', width: 800, format: 'webp' })).src; Defensive patterns
Strategy: validation
Validate before calling
// before proxying/rewriting any /_image request, keep the required params intact
const u = new URL(request.url);
if (!u.searchParams.has('href')) {
return new Response('Missing href param', { status: 400 });
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Configured image service is not a local service
- ⚠️ Astro could not optimize image
- Astro couldn't find the correct page to render, probably…
- Astro couldn't find the correct page to render, probably…
- ⚠️ Astro expected an SVG for
AI-assisted analysis of withastro/astro@e294953aa8 (2026-09-09).
Data as JSON: /api/errors/6aca2d5b57c3e965.
Report an issue: GitHub.
Appendix: source
Thrown at packages/astro/src/assets/endpoint/generic.ts:26
import { etag } from '../utils/etag.js';
import { loadImage } from './loadImage.js';
/**
* Endpoint used in dev and SSR to serve optimized images by the base image services
*/
export const GET: APIRoute = async ({ request, logger }) => {
try {
const imageService = await getConfiguredImageService();
if (!('transform' in imageService)) {
throw new Error('Configured image service is not a local service');
}
const url = new URL(request.url);
const transform = await imageService.parseURL(url, imageConfig, logger);
if (!transform?.src) {
throw new Error('Incorrect transform returned by `parseURL`');
}
let inputBuffer: ArrayBuffer | undefined = undefined;
const isRemoteImage = isRemotePath(transform.src);
if (isRemoteImage && isRemoteAllowed(transform.src, imageConfig) === false) {
return new Response('Forbidden', { status: 403 });
}
const sourceUrl = new URL(transform.src, url.origin);
// Have we been tricked into thinking this is local?
if (!isRemoteImage && sourceUrl.origin !== url.origin) {
return new Response('Forbidden', { status: 403 });
}
inputBuffer = await loadImage(View on GitHub (pinned to e294953aa8)