withastro/astro · error · MarkdocError

**${String(rootRelativePath)}** contains invalid content: -

Error message

**${String(rootRelativePath)}** contains invalid content:
- ${e.error.message}

What it means

The Markdoc integration runs `Markdoc.validate` on each content file at build time and collects real errors, deliberately ignoring `variable-undefined` (runtime-configurable variables) and missing-partial errors (resolved later). Any remaining validation error — bad tag usage, invalid attribute types, malformed structure — fails the build with a per-file bullet list, pointing the error overlay at the first offending line.

Source

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

	astroConfig: AstroConfig;
	filePath: string;
}) {
	const validationErrors = Markdoc.validate(ast, markdocConfig).filter((e) => {
		return (
			(e.error.level === 'error' || e.error.level === 'critical') &&
			// Ignore `variable-undefined` errors.
			// Variables can be configured at runtime,
			// so we cannot validate them at build time.
			e.error.id !== 'variable-undefined' &&
			// Ignore missing partial errors.
			// We will resolve these in `resolvePartials`.
			!(e.error.id === 'attribute-value-invalid' && /^Partial .+ not found/.test(e.error.message))
		);
	});

	if (validationErrors.length) {
		const rootRelativePath = path.relative(fileURLToPath(astroConfig.root), filePath);
		throw new MarkdocError({
			message: [
				`**${String(rootRelativePath)}** contains invalid content:`,
				...validationErrors.map((e) => `- ${e.error.message}`),
			].join('\n'),
			location: {
				// Error overlay does not support multi-line or ranges.
				// Just point to the first line.
				line: validationErrors[0].lines[0],
				file: viteId,
			},
		});
	}
}

function getUsedTags(markdocAst: Node) {
	const tags = new Set<string>();
	const validationErrors = Markdoc.validate(markdocAst);
	// Hack: run the validator with an empty config and look for 'tag-undefined'.

View on GitHub (pinned to 52e6c34790)

Solutions

  1. Read the bulleted Markdoc validation messages — they name the exact tag/attribute problem.
  2. Open the file at the reported line and fix the markup (add the tag to config, fix the attribute value, fix nesting).
  3. If the construct is intentionally valid, adjust the Markdoc config schema (nodes/tags/functions) to permit it.
  4. Re-run the dev server to confirm the overlay clears before rebuilding.

Example fix

{% note type="warning" %}...{% /note %}
<!-- schema expects type in ['info','danger'] -->

{% note type="danger" %}...{% /note %}
Defensive patterns

Strategy: validation

Validate before calling

// Validate content in CI with the same engine the build uses
import Markdoc from '@markdoc/markdoc';
import config from './markdoc.config.mjs';
import { globSync } from 'glob';
import fs from 'node:fs';
for (const f of globSync('src/**/*.mdoc')) {
  const errors = Markdoc.validate(Markdoc.parse(fs.readFileSync(f, 'utf8')), config)
    .filter((e) =>
      e.error.id !== 'variable-undefined' &&
      !(e.error.id === 'attribute-value-invalid' && /^Partial .+ not found/.test(e.error.message)));
  if (errors.length) throw new Error(`${f}:\n${errors.map((e) => `- ${e.error.message}`).join('\n')}`);
}

Prevention

When it happens

Trigger: A .mdoc file using an unregistered tag, passing a wrong-typed attribute to a schema (e.g. a string where Number is required), or nesting nodes outside their allowed `children` (e.g. block tag inside inline context).

Common situations: Authoring mistakes in content collections; schema/config drift after renaming tags or tightening attribute types; contributors unfamiliar with the allowed markup.

Related errors


AI-assisted analysis of withastro/astro@52e6c34790 (2026-08-18). Data as JSON: /api/errors/65ac6dbed1c00683. Report an issue: GitHub.