withastro/astro · error · AstroError

InvalidFrontmatterInjectionError

InvalidFrontmatterInjectionError

Error message

A remark or rehype plugin attempted to inject invalid frontmatter. Ensure "astro.frontmatter" is set to a valid JSON object that is not `null` or `undefined`.

What it means

Remark/rehype plugins can inject markdown frontmatter by writing data.astro.frontmatter on the file/mdast. After the markdown renders, Astro validates the resulting frontmatter is a usable plain object; if a plugin set it to null, undefined, an array, or another non-object, InvalidFrontmatterInjectionError is thrown. The guard exists because that frontmatter is later merged into the page's own frontmatter object.

Solutions

  1. In your plugin, only assign a plain object — skip the assignment entirely when there is nothing to inject
  2. When merging, spread into the existing value: file.data.astro.frontmatter = { ...file.data.astro.frontmatter, ...newData }
  3. Log data.astro.frontmatter after your plugin runs to confirm it stays a plain JSON object

Example fix

// before — remark plugin
export function myPlugin() {
	return (tree, file) => {
		file.data.astro.frontmatter = undefined; // or a failed lookup result
	};
}

// after
export function myPlugin() {
	return (tree, file) => {
		file.data.astro.frontmatter = { readingTime: 5 }; // plain object only
	};
}
Defensive patterns

Strategy: validation

Validate before calling

// inside your remark/rehype plugin — validate before injecting
const isValidFrontmatter = (fm: unknown): fm is Record<string, unknown> =>
	fm !== null && typeof fm === 'object' && !Array.isArray(fm) &&
	Object.values(fm).every((v) => typeof v !== 'function');

if (newData && isValidFrontmatter(newData)) {
	file.data.astro.frontmatter = { ...file.data.astro.frontmatter, ...newData };
}

Type guard

const isValidFrontmatter = (fm: unknown): fm is Record<string, unknown> =>
	fm !== null && typeof fm === 'object' && !Array.isArray(fm);

Prevention

When it happens

Trigger: A remark plugin assigning file.data.astro.frontmatter = someVar where someVar is null/undefined (e.g. a failed lookup); assigning an array or string instead of a plain object; a second plugin overwriting a valid value with an invalid one.

Common situations: Custom remark plugins for SEO/reading-time metadata that conditionally skip injection by assigning undefined; porting rehype plugins that treat data as free-form.

Related errors


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

Appendix: source

Thrown at packages/astro/src/vite-plugin-markdown/index.ts:98

				if (!renderer) {
					const { markdown: md, image } = settings.config;
					renderer = md.processor.createRenderer({
						image,
						syntaxHighlight: md.syntaxHighlight,
						shikiConfig: md.shikiConfig,
						gfm: md.gfm,
						smartypants: md.smartypants,
					});
				}

				const renderResult = await (await renderer).render(raw.content, {
					fileURL,
					frontmatter: raw.frontmatter,
				});

				// Improve error message for invalid astro frontmatter
				if (!isFrontmatterValid(renderResult.metadata.frontmatter)) {
					throw new AstroError(AstroErrorData.InvalidFrontmatterInjectionError);
				}

				let html = renderResult.code;
				const {
					headings,
					localImagePaths: rawLocalImagePaths,
					remoteImagePaths,
					frontmatter,
				} = renderResult.metadata;

				// Add default charset for markdown pages
				const isMarkdownPage = isPage(fileURL, settings);
				const charset = isMarkdownPage ? '<meta charset="utf-8">' : '';

				// Resolve all the extracted images from the content
				const localImagePaths: MarkdownImagePath[] = [];
				for (const imagePath of rawLocalImagePaths) {
					localImagePaths.push({

View on GitHub (pinned to 52e6c34790)