withastro/astro · error · MarkdocError
Could not resolve image
Error message
Could not resolve image ${JSON.stringify(node.attributes.src)} from ${JSON.stringify(ctx.filePath)}. Does the file exist? What it means
While transforming Markdoc content, the integration tries to resolve each image node's `src` through Astro's asset pipeline (emitting optimized images). If `src` cannot be resolved to a local file, it throws this MarkdocError echoing the raw src and the containing file path. Only resolvable local images and handled component forms make it through; an unresolvable path aborts the build.
Solutions
- Check that the path in the error exists, relative to the content file shown in the message.
- Fix the relative prefix (`./`, `../`) or move the image into the referenced location.
- For remote images, use the full URL form supported by the {% image %} component path rather than a bare path.
- Watch for case mismatches between the filename in content and on disk.
Example fix
{% image src="../images/hero.png" alt="Hero" /%}
{% image src="./images/hero.png" alt="Hero" /%} Defensive patterns
Strategy: validation
Validate before calling
// Verify image sources referenced by .mdoc files exist before build
import { Markdoc } from '@markdoc/markdoc';
import { globSync } from 'glob';
import fs from 'node:fs';
import path from 'node:path';
for (const f of globSync('src/**/*.mdoc')) {
for (const node of Markdoc.parse(fs.readFileSync(f, 'utf8')).walk()) {
if (node.type !== 'image' && node.tag !== 'image') continue;
const src = node.attributes?.src;
if (typeof src === 'string' && !/^(https?:)?\/\//.test(src)) {
if (!fs.existsSync(path.resolve(path.dirname(f), src))) {
throw new Error(`${f}: image not found: ${src}`);
}
}
}
} Prevention
- Store content-adjacent images at a fixed relative depth and document the convention.
- Add an asset-existence lint to CI (also catches case mismatches on Linux runners).
- Use remote URLs or the {% image %} component path for images outside the content tree.
When it happens
Trigger: An image node / `{% image %}` tag in a .mdoc file whose `src` points to a nonexistent local file — wrong relative path, moved asset, missing extension — while not matching the component/remote-URL escape hatches.
Common situations: Content authors referencing images from a different directory depth; assets moved during refactors; case-sensitivity mismatches on case-sensitive filesystems/CI.
Related errors
- Failed to get static images from the Cloudflare prerender…
- Failed to parse image reference
- ** ** contains invalid content: Could not read partial file…
- ** ** contains invalid content
- (Uncaught error) Partial tag requires a 'file' attribute
AI-assisted analysis of withastro/astro@52e6c34790 (2026-08-18).
Data as JSON: /api/errors/6e06a4c56d530144.
Report an issue: GitHub.
Appendix: 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 52e6c34790)