withastro/astro · error · MarkdocError

**${relativePartialPath}** contains invalid content: Could n

Error message

**${relativePartialPath}** contains invalid content:
Could not read partial file `${file}`. Does the file exist?

What it means

During partial resolution the integration tries to resolve the `file` value via the Rolldown plugin context (`pluginContext.resolve`), falls back to `./` + file, and reads the resolved path with `fs.promises.readFile`. If resolution returns no id or reading throws, a `MarkdocError` is raised naming the partial and noting the file may not exist.

Source

Thrown at packages/integrations/markdoc/src/content-entry-type.ts:202

			let partialPath: string;
			let partialContents: string;
			try {
				const resolved = await pluginContext.resolve(file, fileURLToPath(fileUrl));
				let partialId = resolved?.id;
				if (!partialId) {
					const attemptResolveAsRelative = await pluginContext.resolve(
						'./' + file,
						fileURLToPath(fileUrl),
					);
					if (!attemptResolveAsRelative?.id) throw new Error();
					partialId = attemptResolveAsRelative.id;
				}

				partialPath = fileURLToPath(new URL(prependForwardSlash(partialId), 'file://'));
				partialContents = await fs.promises.readFile(partialPath, 'utf-8');
			} catch {
				throw new MarkdocError({
					message: [
						`**${String(relativePartialPath)}** contains invalid content:`,
						`Could not read partial file \`${file}\`. Does the file exist?`,
					].join('\n'),
				});
			}
			if (pluginContext.meta.watchMode) pluginContext.addWatchFile(partialPath);
			let partialTokens = tokenizer.tokenize(partialContents);
			if (allowHTML) {
				partialTokens = htmlTokenTransform(tokenizer, partialTokens);
			}
			const partialAst = Markdoc.parse(partialTokens);
			raisePartialValidationErrors(partialAst, partialPath);
			await resolvePartials({
				ast: partialAst,
				root,
				fileUrl: pathToFileURL(partialPath),
				tokenizer,

View on GitHub (pinned to d081033d5f)

Solutions

  1. Verify the partial file exists at the path declared in `file=`.
  2. Use a path that resolves relative to the `.mdoc` file importing it (prefix with `./` if needed).
  3. Check filesystem case matches exactly on case-sensitive build environments.
  4. Confirm the partial is inside the Astro project root or an allowed Vite dir.

Example fix

{% partial file="./_nav.mdoc" / %}
Defensive patterns

Strategy: validation

Validate before calling

import { pathExists } from 'fs-extra';
async function partialExists(file: string, from: string) {
  const resolved = path.resolve(path.dirname(from), file);
  return await pathExists(resolved);
}

Type guard

async function canReadPartial(file: string, baseDir: string): Promise<boolean> {
  try {
    await fs.promises.access(path.resolve(baseDir, file));
    return true;
  } catch { return false; }
}

Try / catch

try {
  await resolvePartial(file, fileUrl);
} catch (e) {
  console.error(`Could not read partial ${file} from ${fileUrl}`);
  throw e;
}

Prevention

When it happens

Trigger: Referencing `{% partial file="./missing.mdoc" / %}` where the file does not exist on disk. Path that cannot be resolved by Rolldown/Vite from the importing `.mdoc`'s location. Symlink or permission issue during read. Partial path is absolute or non-relative and resolve fails both attempts.

Common situations: Typo in the partial path. Moved/deleted a partial without updating references. Importing a partial from a directory not covered by Vite's `fs.allow`. Case-sensitivity mismatch between macOS dev and Linux build.

Related errors


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