withastro/astro · error · AstroError
ImageNotFound
ImageNotFound
Error message
Could not find requested image `${imagePath}`. Does it exist? What it means
Thrown by the content assets Vite plugin when a content collection image import cannot be resolved by Vite's module resolver. The plugin's `resolveId` hook intercepts IDs flagged with `CONTENT_IMAGE_FLAG`, strips the flag, and delegates to `this.resolve()`. If resolution returns null (file does not exist or path is wrong), the error is thrown with the base image path.
Source
Thrown at packages/astro/src/content/vite-plugin-content-assets.ts:57
name: 'astro:content-asset-propagation',
enforce: 'pre',
resolveId: {
filter: {
id: new RegExp(`(?:\\?|&)(?:${CONTENT_IMAGE_FLAG}|${CONTENT_RENDER_FLAG})(?:&|=|$)`),
},
async handler(id, importer, opts) {
if (hasContentFlag(id, CONTENT_IMAGE_FLAG)) {
const [base, query] = id.split('?');
const params = new URLSearchParams(query);
const importerParam = params.get('importer');
const importerPath = importerParam
? fileURLToPath(new URL(importerParam, settings.config.root))
: importer;
const resolved = await this.resolve(base, importerPath, { skipSelf: true, ...opts });
if (!resolved) {
throw new AstroError({
...AstroErrorData.ImageNotFound,
message: AstroErrorData.ImageNotFound.message(base),
});
}
// Preserve the content image flag in the resolved ID so that downstream plugins
// (e.g. astro:assets:esm) can detect content collection images and avoid creating
// full SVG components, which would import from the server runtime and cause a
// circular module dependency deadlock when combined with top-level await (TLA).
resolved.id = `${resolved.id}?${CONTENT_IMAGE_FLAG}`;
return resolved;
}
if (hasContentFlag(id, CONTENT_RENDER_FLAG)) {
const base = id.split('?')[0];
for (const { extensions, handlePropagation = true } of settings.contentEntryTypes) {
if (handlePropagation && extensions.includes(extname(base))) {
return this.resolve(`${base}?${PROPAGATED_ASSET_FLAG}`, importer, {
skipSelf: true,View on GitHub (pinned to d081033d5f)
Solutions
- Verify the image path in the error message exists on disk, exactly as spelled (case-sensitive).
- Ensure the path is relative to the content entry file, not the project root.
- If using an alias like `@assets/`, confirm it is mapped in `tsconfig.json` compilerOptions.paths.
- Check `git status` to confirm the image is committed if the error occurs in CI.
- Add the correct file extension (.png, .jpg, .webp, .svg).
Example fix
<!-- before -->  <!-- after --> 
Defensive patterns
Strategy: validation
Validate before calling
import { existsSync } from 'fs';
import { resolve, dirname } from 'path';
function imageExists(fromFilePath: string, imagePath: string): boolean {
const resolved = resolve(dirname(fromFilePath), imagePath);
return existsSync(resolved);
} Prevention
- Use absolute aliases (configured in tsconfig paths) for image imports to avoid relative path errors.
- Commit images alongside content in the same PR.
- Run CI on Linux to catch case-sensitivity issues before deployment.
When it happens
Trigger: A content entry (Markdown/MDX) references an image via a relative path or `@assets/` alias that does not resolve to an actual file. The `CONTENT_IMAGE_FLAG` query param triggers the handler, `this.resolve(base, importerPath)` returns null, and `ImageNotFound` is thrown.
Common situations: Typo in the image filename or extension. Image was deleted or not yet committed. Relative path is wrong relative to the entry file's location. Using an alias that isn't configured in `tsconfig.json` paths or Vite config. Case-sensitivity mismatch on Linux CI vs. macOS dev.
Related errors
- UnknownContentCollectionError
- PluginContentImportsError
- Could not resolve image ${JSON.stringify(node.attributes.src
- Unsupported image format "${options.format}"
- ExpectedImageOptions
AI-assisted analysis of withastro/astro@d081033d5f (2026-08-12).
Data as JSON: /api/errors/9f8c55dd09880438.
Report an issue: GitHub.