withastro/astro · error · MarkdocError
Could not resolve image ${JSON.stringify(node.attributes.src
Error message
Could not resolve image ${JSON.stringify(node.attributes.src)} from ${JSON.stringify(ctx.filePath)}. Does the file exist? What it means
When resolving an image node in Markdoc, the integration tries to compute a resolved source via the Astro image pipeline. If resolution returns nothing (no `src`), it throws `MarkdocError` showing the unresolved `node.attributes.src` and the originating `ctx.filePath`. Static-output builds also add the path to `globalThis.astroAsset.referencedImages` on success.
Source
Thrown at packages/integrations/markdoc/src/content-entry-type.ts:337
? undefined
: (opts: Parameters<typeof ctx.pluginContext.emitFile>[0]) =>
emitClientAsset(ctx.pluginContext, opts);
const src = await emitImageMetadata(resolved.id, fileEmitter);
const fsPath = resolved.id;
if (src) {
// We cannot track images in Markdoc, Markdoc rendering always strips out the proxy. As such, we'll always
// assume that the image is referenced elsewhere, to be on safer side.
if (ctx.astroConfig.output === 'static') {
if (globalThis.astroAsset.referencedImages)
globalThis.astroAsset.referencedImages.add(fsPath);
}
node.attributes[attributeName] = { ...src, fsPath };
}
} else {
throw new MarkdocError({
message: `Could not resolve image ${JSON.stringify(
node.attributes.src,
)} from ${JSON.stringify(ctx.filePath)}. Does the file exist?`,
});
}
} else if (isComponent) {
// If the user is using the {% image %} tag, always pass the `src` attribute as `__optimizedSrc`, even if it's an external URL or absolute path.
// That way, the component can decide whether or not to optimize it.
node.attributes[attributeName] = node.attributes.src;
}
}
await emitOptimizedImages(node.children, ctx);
}
}
function shouldOptimizeImage(src: string) {
// Optimize anything that is NOT external or an absolute path to `public/`
return !isValidUrl(src) && !src.startsWith('/');View on GitHub (pinned to d081033d5f)
Solutions
- Confirm the image file exists at the path used in `src`.
- For local images use a relative path from the `.mdoc` file (e.g. `src="../../assets/hero.png"`).
- For remote images verify the URL is reachable at build time.
- Ensure the asset lives under a directory covered by Vite's `fs.allow`.
Example fix
{% image src="../../assets/hero.png" alt="Hero" / %} Defensive patterns
Strategy: validation
Validate before calling
import { pathExists } from 'fs-extra';
async function imageSourceOk(src: string, baseDir: string) {
if (/^https?:\/\//.test(src)) return true;
return pathExists(path.resolve(baseDir, src));
} Type guard
function isResolvableImageSrc(src: unknown): src is string {
return typeof src === 'string' && src.length > 0;
} Try / catch
try {
await resolveImage(node, ctx);
} catch (e) {
ctx.logger?.error?.(`Image ${node.attributes.src} could not be resolved from ${ctx.filePath}`);
throw e;
} Prevention
- Use relative paths from the `.mdoc` for local images.
- Verify remote image URLs are reachable at build time.
- Keep image assets under Vite `fs.allow`.
When it happens
Trigger: Using `{% image src="/missing.png" / %}` or `{% image src="./nope.jpg" / %}` where the image cannot be resolved by the Astro asset service from the Markdoc file's location. Remote image that fails fetch. Image import path that does not resolve through Vite.
Common situations: Image file missing or moved. Wrong relative path from the `.mdoc`. Image outside the project's allowed directories. Non-existent remote URL during build.
Related errors
- ImageNotFound
- Failed to get static images from the Cloudflare prerender se
- **${relativePartialPath}** contains invalid content: Could n
- **${rootRelativePath}** contains invalid content: ${validati
- Unexpected problem adding heading IDs to Markdoc file. Did y
AI-assisted analysis of withastro/astro@d081033d5f (2026-08-12).
Data as JSON: /api/errors/6e06a4c56d530144.
Report an issue: GitHub.