withastro/astro · error · AstroError

ImageMissingAlt

ImageMissingAlt

Error message

Image missing "alt" property. "alt" text is required to describe important images on the page.

What it means

AstroError with code `ImageMissingAlt` (same code as the `<Image>` component), thrown by the `<Picture>` component at the same point in its render: after destructuring props it checks `props.alt` and throws if it is `undefined` or `null`. `<Picture>` generates multiple `<source>`s plus a fallback `<img>`, all of which inherit the same accessibility requirement, so the `alt` guard is identical.

Source

Thrown at packages/astro/components/Picture.astro:38

	formats?: ImageOutputFormat[];
	fallbackFormat?: ImageOutputFormat;
	pictureAttributes?: HTMLAttributes<'picture'>;
};

const defaultFormats = ['webp'] as const;
const defaultFallbackFormat = 'png' as const;

// Certain formats don't want PNG fallbacks:
// - GIF will typically want to stay as a gif, either for animation or for the lower amount of colors
// - SVGs can't be converted to raster formats in most cases
// - JPEGs compress photographs and high-noise images better than PNG in most cases
// For those, we'll use the original format as the fallback instead.
const specialFormatsFallback = ['gif', 'svg', 'jpg', 'jpeg'] as const;

const { formats = defaultFormats, pictureAttributes = {}, fallbackFormat, ...props } = Astro.props;

if (props.alt === undefined || props.alt === null) {
	throw new AstroError(AstroErrorData.ImageMissingAlt);
}

// Picture attribute inherit scoped styles from class and attributes
const scopedStyleClass = props.class?.match(/\bastro-\w{8}\b/)?.[0];
if (scopedStyleClass) {
	if (pictureAttributes.class) {
		pictureAttributes.class = `${pictureAttributes.class} ${scopedStyleClass}`;
	} else {
		pictureAttributes.class = scopedStyleClass;
	}
}

const layout = props.layout ?? imageConfig.layout ?? 'none';
const useResponsive = layout !== 'none';

if (useResponsive) {
	props.layout ??= imageConfig.layout;
	props.fit ??= imageConfig.objectFit ?? 'cover';

View on GitHub (pinned to d081033d5f)

Solutions

  1. Add a descriptive `alt` prop to `<Picture>`.
  2. Use `alt=""` for purely decorative pictures.
  3. Guard dynamic values: `alt={data.alt ?? ''}`.
  4. Run `astro check` to surface missing props before render.

Example fix

// before
<Picture src={hero} formats={['avif','webp']} />

// after
<Picture src={hero} formats={['avif','webp']} alt="Hero illustration of the product" />
Defensive patterns

Strategy: type-guard

Validate before calling

function hasAlt(props: unknown): props is { alt: string } {
  return props != null && typeof (props as { alt?: unknown }).alt === 'string';
}
// before rendering
if (!hasAlt(props)) props.alt = data.alt ?? '';

Type guard

function hasAltProp(props: unknown): props is { alt: string } {
  return props != null && typeof (props as { alt?: unknown }).alt === 'string';
}

Prevention

When it happens

Trigger: Calling `<Picture src={...} />` (or `<Picture formats={[...]} />`) without an `alt` prop; destructuring that drops `alt`; passing `alt={null}`.

Common situations: A developer swaps `<Image>` for `<Picture>` to serve modern formats and forgets to carry over `alt`; CMS data where alt text is optional; template/partial that omits the prop.

Related errors


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