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

  1. Confirm the image file exists at the path used in `src`.
  2. For local images use a relative path from the `.mdoc` file (e.g. `src="../../assets/hero.png"`).
  3. For remote images verify the URL is reachable at build time.
  4. 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

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


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