withastro/astro · error · Error

Configured image service is not a local service

Error message

Configured image service is not a local service

What it means

Same 'transform' guard as the generic dev endpoint, but in the shared handleImageRequest used across adapters/build paths. It calls getConfiguredImageService() and requires a LocalImageService; an external service (no transform) throws a plain Error.

Source

Thrown at packages/astro/src/assets/endpoint/shared.ts:45

		}

		return Buffer.from(await res.arrayBuffer());
	} catch {
		return undefined;
	}
}

export const handleImageRequest = async ({
	request,
	loadLocalImage,
}: {
	request: Request;
	loadLocalImage: (src: string, baseUrl: URL) => Promise<Buffer | undefined>;
}) => {
	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);

	if (!transform?.src) {
		return new Response('Invalid request', { status: 400 });
	}

	// Reject requests that attempt to convert a non-SVG source to SVG output.
	// This mirrors the same guard in verifyOptions() that protects the <Image> component path.
	if (transform.format === 'svg') {
		const sourceFormat = inferSourceFormat(transform.src);
		if (sourceFormat !== 'svg') {
			return new Response('Cannot convert non-SVG source to SVG format', { status: 403 });
		}
	}

View on GitHub (pinned to d081033d5f)

Solutions

  1. Configure a local service (e.g. sharp) for paths that go through handleImageRequest.
  2. For external/CDN services, rely on URL generation and avoid the local optimization handler.
  3. Ensure a custom service implements transform.

Example fix

// before - external service for an SSR build that hits the shared handler
image: { service: passthroughImageService }
// after
image: { service: sharpImageService }
Defensive patterns

Strategy: validation

Validate before calling

// Before relying on handleImageRequest, confirm the configured service is local.
import { isLocalService } from 'astro/assets';
const svc = await getConfiguredImageService();
if (!isLocalService(svc)) throw new Error('Shared image handler requires a local service.');

Type guard

function isLocalImageService(s: any): boolean {
  return s && typeof s === 'object' && typeof s.transform === 'function';
}

Prevention

When it happens

Trigger: An adapter or build/hybrid path that routes image requests through handleImageRequest while an external image service is configured.

Common situations: SSR adapter + external service where the shared handler is invoked; build-time optimization expecting a local service; custom service missing transform.

Related errors


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