withastro/astro · error · AstroError

RemoteImageNotAllowed

RemoteImageNotAllowed

Error message

Remote image ${imageURL} is not allowed by your image configuration.

What it means

Thrown by getImage() when inferSize is set, the resolved src is a remote image string, and isRemoteAllowed(src, imageConfig) returns false. Astro requires remote image hosts to be explicitly allow-listed (image.domains / image.remotePatterns) before it will probe a remote URL for dimensions.

Source

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

	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)) {
			if (!isRemoteAllowed(resolvedOptions.src, imageConfig)) {
				throw new AstroError({
					...AstroErrorData.RemoteImageNotAllowed,
					message: AstroErrorData.RemoteImageNotAllowed.message(resolvedOptions.src),
				});
			}

			const getRemoteSize = (url: string) =>
				service.getRemoteSize?.(url, imageConfig) ?? inferRemoteSize(url, imageConfig);
			const result = await getRemoteSize(resolvedOptions.src); // Directly probe the image URL
			resolvedOptions.width ??= result.width;
			resolvedOptions.height ??= result.height;
			// We've already paid for the fetch; reuse it to pin down the output format so the URL
			// (and any baked filename) doesn't have to defer or refetch.
			if (result.format) {
				resolvedOptions.format ??= resolveDefaultOutputFormat(result.format);
			}
			originalWidth = result.width;
			originalHeight = result.height;
		}

View on GitHub (pinned to d081033d5f)

Solutions

  1. Add the host to image.domains: image: { domains: ['other-host'] }.
  2. Or add a remotePatterns entry: image: { remotePatterns: [{ protocol: 'https', hostname: 'other-host' }] }.
  3. If the image is local, import it as an ESM asset instead of using a URL.
  4. Drop inferSize and supply explicit width/height to avoid the remote probe entirely.

Example fix

// astro.config.mjs
// before
image: { domains: ['cdn.example.com'] }

// after
image: { domains: ['cdn.example.com', 'other-host'] }
Defensive patterns

Strategy: validation

Validate before calling

import { matchPattern } from 'astro/assets'; // pseudo
function isAllowedRemote(src: string, cfg: { domains?: string[]; remotePatterns?: any[] }): boolean {
  const host = (() => { try { return new URL(src).hostname; } catch { return null; } })();
  if (!host) return false;
  return (cfg.domains ?? []).includes(host);
}

Prevention

When it happens

Trigger: Calling getImage({ src: 'https://other-host/img.png', inferSize: true }) where 'other-host' is not in image.domains and does not match any image.remotePatterns. Only remote (string) paths that pass isRemotePath are checked against isRemoteAllowed.

Common situations: Author adds a new image CDN/hostname without updating astro.config image.domains or remotePatterns, switches from a local import to a remote URL, or copies an image URL from a third party.

Related errors


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