withastro/astro · error · AstroError

ExpectedNotESMImage

ExpectedNotESMImage

Error message

An ESM-imported image cannot be passed directly to `getImage()`. Instead, pass an object with the image in the `src` property.

What it means

Thrown by getImage() when the entire options object is itself an ESM-imported image metadata object rather than a transform containing src. isImageMetadata(options) detects the non-enumerable fsPath marker that Astro injects on imports, meaning the user passed the imported image directly instead of wrapping it in { src }.

Source

Thrown at packages/astro/src/assets/internal.ts:70

	if (!options || typeof options !== 'object') {
		throw new AstroError({
			...AstroErrorData.ExpectedImageOptions,
			message: AstroErrorData.ExpectedImageOptions.message(JSON.stringify(options)),
		});
	}
	if (typeof options.src === 'undefined') {
		throw new AstroError({
			...AstroErrorData.ExpectedImage,
			message: AstroErrorData.ExpectedImage.message(
				options.src,
				'undefined',
				JSON.stringify(options),
			),
		});
	}

	if (isImageMetadata(options)) {
		throw new AstroError(AstroErrorData.ExpectedNotESMImage);
	}

	const service = await getConfiguredImageService();

	// If the user inlined an import, something fairly common especially in MDX, or passed a function that returns an Image, await it for them
	const resolvedOptions: ImageTransform = {
		...options,
		src: await resolveSrc(options.src),
	};

	let originalWidth: number | undefined;
	let originalHeight: number | undefined;

	// Infer size for remote images if inferSize is true
	if (resolvedOptions.inferSize) {
		delete resolvedOptions.inferSize; // Delete so it doesn't end up in the attributes

		if (isRemoteImage(resolvedOptions.src) && isRemotePath(resolvedOptions.src)) {

View on GitHub (pinned to d081033d5f)

Solutions

  1. Wrap the imported image in a src property: getImage({ src: importedImage }).
  2. If using <Image />, use <Image src={importedImage} />, not <Image {...importedImage} />.
  3. Audit MDX components that spread imported images into getImage.
  4. Add a lint/type check that the first getImage argument is a transform, not an ImageMetadata.

Example fix

// before
import photo from './photo.jpg';
const img = getImage(photo);

// after
import photo from './photo.jpg';
const img = getImage({ src: photo, width: 800, height: 600 });
Defensive patterns

Strategy: type-guard

Validate before calling

import { isImageMetadata } from 'astro/assets';
function isBareImageMetadata(v: unknown): boolean {
  return isImageMetadata(v);
}

Type guard

import type { ImageMetadata } from 'astro';
function isImageMetadataValue(v: unknown): v is ImageMetadata {
  return typeof v === 'object' && v !== null && 'fsPath' in v;
}

Prevention

When it happens

Trigger: Calling getImage(importedImage) instead of getImage({ src: importedImage }). The imported asset module's metadata object (with the hidden fsPath) is passed where a transform object is expected.

Common situations: Common in MDX/JSX when an author writes getImage(myImage) rather than getImage({ src: myImage }), or passes the result of an `import myImage from '...'` directly as the first argument.

Related errors


AI-assisted analysis of withastro/astro@d081033d5f (2026-08-12). Data as JSON: /api/errors/23825330b0dd4d29. Report an issue: GitHub.