withastro/astro · error · Error

[MDX] A remark or rehype plugin attempted to inject invalid…

Error message

[MDX] 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 frontmatter into an MDX file by setting `vfile.data.astro.frontmatter`. The MDX integration later re-exports it as `export const frontmatter = ...` and derives layout wiring from it, so the value must be a non-null, non-undefined JSON object. This rehype plugin throws when that data is missing or fails a validity check (e.g. null, a primitive, an array). The message is copied from Astro core's equivalent markdown error.

Solutions

  1. In your plugin, always assign a plain object: `file.data.astro.frontmatter = { ...file.data.astro.frontmatter, myKey: value }`.
  2. Ensure the injecting plugin runs before rehypeApplyFrontmatterExport (correct remarkPlugins/rehypePlugins order).
  3. Keep values JSON-serializable (no functions, Dates are fine but Class instances are not).
  4. Update @astrojs/mdx and astro — plugin pipeline ordering fixes have landed across versions.

Example fix

// before — plugin injects invalid frontmatter
file.data.astro.frontmatter = frontmatterYamlString;

// after — merge a plain object
file.data.astro.frontmatter = {
  ...file.data.astro.frontmatter,
  ...parseYaml(frontmatterYamlString),
};
Defensive patterns

Strategy: type-guard

Type guard

type AstroVFileData = { astro?: { frontmatter?: unknown } };
function isValidFrontmatter(v: unknown): v is Record<string, unknown> {
  return typeof v === 'object' && v !== null && !Array.isArray(v);
}
// Inside your remark/rehype plugin, before assignment:
// const fm = (file.data as AstroVFileData).astro?.frontmatter;
// if (!isValidFrontmatter(fm)) { /* assign a fresh object instead */ }

Prevention

When it happens

Trigger: A custom remark/rehype plugin sets `file.data.astro.frontmatter` to a string/number/boolean/array/null, or deletes/never sets it while another plugin assumes it exists; plugin ordering puts this rehype plugin before the frontmatter-injecting one.

Common situations: Writing a frontmatter-injection plugin (e.g. reading title/date from filesystem or CMS) and assigning a non-object; upgrading remark plugins that changed data placement; copy-pasting plugin snippets that mutate vfile.data incorrectly.

Related errors


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

Appendix: source

Thrown at packages/integrations/mdx/src/rehype-apply-frontmatter-export.ts:23

import type { VFile } from 'vfile';
import { jsToTreeNode } from './utils.js';

// Passed metadata to help determine adding charset utf8 by default
declare module 'vfile' {
	interface DataMap {
		applyFrontmatterExport?: {
			srcDir?: URL;
		};
	}
}

const exportConstPartialTrueRe = /export\s+const\s+partial\s*=\s*true/;

export function rehypeApplyFrontmatterExport() {
	return function (tree: Root, vfile: VFile) {
		const frontmatter = vfile.data.astro?.frontmatter;
		if (!frontmatter || !isFrontmatterValid(frontmatter))
			throw new Error(
				// Copied from Astro core `errors-data`
				// TODO: find way to import error data from core
				'[MDX] 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`.',
			);
		const extraChildren: RootContent[] = [
			jsToTreeNode(`export const frontmatter = ${JSON.stringify(frontmatter)};`),
		];
		if (frontmatter.layout) {
			extraChildren.unshift(
				jsToTreeNode(
					// NOTE: Use `__astro_*` import names to prevent conflicts with user code
					/** @see 'vite-plugin-markdown' for layout props reference */
					`\
import { jsx as __astro_layout_jsx__ } from 'astro/jsx-runtime';
import __astro_layout_component__ from ${JSON.stringify(frontmatter.layout)};

export default function ({ children }) {
	const { layout, ...content } = frontmatter;

View on GitHub (pinned to 52e6c34790)