withastro/astro · error · AstroError
GetImageNotUsedOnServer
GetImageNotUsedOnServer
Error message
`getImage()` should only be used on the server. To use images on the client, render the `src` from `getImage()` during the server render, then pass it to the client for usage.
What it means
Astro exposes `getImage()` through a virtual module that is environment-aware. When that module is bundled for the client, `getImage` is replaced by a stub that throws GetImageNotUsedOnServer at call time (vite-plugin-assets.ts:189). Image optimization is a server-side operation; calling it in browser-executed code is unsupported.
Solutions
- Move the `getImage()` call into server-executed code (.astro frontmatter, endpoints, SSR components) and pass the resulting `src`/attributes down as props
- If a shared helper must exist, branch on `import.meta.env.SSR` and only call getImage on the server branch
- For client-driven images, use plain <img> with a URL the server already prepared
Example fix
// before (client island)
import { getImage } from 'astro/assets';
const img = await getImage({ src: hero }); // throws in browser
// after (server component passes result down)
// Server.astro: const img = await getImage({ src: hero });
// <Island imgSrc={img.src} imgAttrs={img.attributes} /> Defensive patterns
Strategy: validation
Validate before calling
// keep getImage out of client bundles by branching on the environment
export async function safeGetImage(options) {
if (!import.meta.env.SSR) {
throw new Error('safeGetImage must only be called during server rendering');
}
const { getImage } = await import('astro/assets');
return getImage(options);
} Type guard
function isServerRender(): boolean {
return import.meta.env.SSR === true;
} Try / catch
try {
const img = await getImage(options);
} catch (err) {
if (err instanceof AstroError && err.code === 'GetImageNotUsedOnServer') {
throw new Error('Move this getImage() call to .astro frontmatter or an API route');
}
throw err;
} Prevention
- Never import getImage in files that are hydrated (islands, client entries)
- Compute image URLs at build/SSR time and pass them to client components as props
When it happens
Trigger: Importing `getImage` (virtual:get-image / astro:assets re-export) inside a hydrated component, client script, or framework component that ships to the browser, then calling it there; a shared utility used by both server pages and client islands that calls getImage at module scope or in an event handler.
Common situations: Refactoring a .astro page into a React/Preact/Svelte island and moving the getImage call along with it; calling getImage inside onClick/useEffect; library code that assumed Node always executes it.
Related errors
- ⚠️ Astro could not optimize image
- ⚠️ Astro expected an SVG for
- Configured image service is not a local service
- Configured image service is not a local service
- FailedToFetchRemoteImageDimensions
AI-assisted analysis of withastro/astro@e294953aa8 (2026-09-09).
Data as JSON: /api/errors/54c2792129d833be.
Report an issue: GitHub.
Appendix: source
Thrown at packages/astro/src/assets/vite-plugin-assets.ts:224
},
load: {
filter: {
id: new RegExp(`^(${RESOLVED_VIRTUAL_MODULE_ID}|${RESOLVED_VIRTUAL_GET_IMAGE_ID})$`),
},
handler(id) {
if (id === RESOLVED_VIRTUAL_GET_IMAGE_ID) {
// Lightweight module exporting only getImage + imageConfig.
// No component references (Image, Picture, Font) to avoid TDZ
// errors when the content runtime and component pages are
// bundled into the same prerender chunk (see #16036).
const isServerEnvironment = isAstroServerEnvironment(this.environment);
const getImageExport = isServerEnvironment
? `${RUNTIME_LOGGER_SETUP}
import { getImage as getImageInternal } from "astro/assets";
export const getImage = async (options) => await getImageInternal(options, imageConfig, _runtimeLogger);`
: `import { AstroError, AstroErrorData } from "astro/errors";
export const getImage = async () => {
throw new AstroError(
AstroErrorData.GetImageNotUsedOnServer.message,
AstroErrorData.GetImageNotUsedOnServer.hint,
);
};`;
const assetQueryParams = settings.adapter?.client?.assetQueryParams
? `new URLSearchParams(${JSON.stringify(
Array.from(settings.adapter.client.assetQueryParams.entries()),
)})`
: 'undefined';
return {
code: `
export const imageConfig = ${JSON.stringify(settings.config.image)};
Object.defineProperty(imageConfig, 'assetQueryParams', {
value: ${assetQueryParams},
enumerable: false,
configurable: true,View on GitHub (pinned to e294953aa8)