withastro/astro · error · Error

Missing `width` parameter for remote image

Error message

Missing `width` parameter for remote image ${options.src}

What it means

Vercel's image service must map each transform to one of the configured sizes. For imported (ESM) images it can infer the nearest configured width from the image's own metadata, but for remote URL sources there is no intrinsic dimension, so options.width is mandatory; a remote Image/getImage without width throws.

Solutions

  1. Pass an explicit width: <Image src={remote} width={1080} ... />
  2. Import the image locally (import hero from './hero.jpg') so dimensions are known and nearest-size fallback applies
  3. If the asset lives on your own domain, consider serving it from the app so it can be imported

Example fix

// before
<Image src="https://cdn.example.com/hero.png" alt="hero" />
// after
<Image src="https://cdn.example.com/hero.png" alt="hero" width={1080} />
Defensive patterns

Strategy: validation

Validate before calling

import { isESMImportedImage } from 'astro/assets/utils';
if (!isESMImportedImage(src) && !transform.width) {
  throw new Error(`Remote image ${src} requires an explicit width`);
}
const img = await getImage({ src, width: transform.width, ...transform });

Type guard

function isRemoteImageSrc(src: unknown): src is string {
  return typeof src === 'string';
} // ESM-imported images are objects; strings are remote URLs needing explicit width

Prevention

When it happens

Trigger: Rendering <Image src='https://cdn.example.com/hero.png' /> with no width/densities, or calling getImage({ src: remoteUrl }) without a width, while the Vercel image service is active.

Common situations: Porting components from the sharp service (which can fetch/measure remote images) to the Vercel adapter; CMS-driven image URLs with unknown dimensions; forgetting that only local imports carry width/height metadata.

Related errors


AI-assisted analysis of withastro/astro@52e6c34790 (2026-08-18). Data as JSON: /api/errors/dccedf880a7ca1e7. Report an issue: GitHub.

Appendix: source

Thrown at packages/integrations/vercel/src/image/shared.ts:139

	// For images where a width has been specified:
	// - If the width that the user asked for isn't in `sizes`, then fall back to the nearest one, but save the width
	// 	the user asked for so we can put it on the `img` tag later.
	// - Otherwise, just use as-is.
	// The end goal is:
	// - The size on the page is always the one that the user asked for or the base image's size
	// - The actual size of the image file is always one of `sizes`, either the one that the user asked for or the nearest to it
	if (!options.width) {
		const src = options.src;
		if (isESMImportedImage(src)) {
			const nearestWidth = configuredWidths.reduce((prev, curr) => {
				return Math.abs(curr - src.width) < Math.abs(prev - src.width) ? curr : prev;
			});

			// Use the image's base width to inform the `width` and `height` on the `img` tag
			options.inputtedWidth = src.width;
			options.width = nearestWidth;
		} else {
			throw new Error(`Missing \`width\` parameter for remote image ${options.src}`);
		}
	} else {
		if (!configuredWidths.includes(options.width)) {
			const nearestWidth = configuredWidths.reduce((prev, curr) => {
				return Math.abs(curr - options.width!) < Math.abs(prev - options.width!) ? curr : prev;
			});

			// Save the user's requested width to inform the `width` and `height` on the `img` tag
			options.inputtedWidth = options.width;
			options.width = nearestWidth;
		}
	}

	if (options.widths) {
		// Vercel only supports a fixed set of widths, so remove any that aren't in the list
		options.widths = options.widths.filter((w) => configuredWidths.includes(w));
		// Oh no, we've removed all the widths! Let's add the nearest one back in
		if (options.widths.length === 0) {

View on GitHub (pinned to 52e6c34790)