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
`rehype-apply-frontmatter-export` reads `vfile.data.astro.frontmatter` and, if missing or failing `isFrontmatterValid`, throws a plain `Error`. The plugin then emits `export const frontmatter = {...}` (and a layout import) into the MDX AST. The check guards against remark/rehype plugins that set `astro.frontmatter` to `null`/`undefined` or a non-JSON-serializable value.
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 d081033d5f)
Solutions
- Ensure any remark/rehype plugin touching frontmatter sets `file.data.astro.frontmatter` to a plain JSON-serializable object (never null/undefined).
- Check plugin order in `remarkPlugins`/`rehypePlugins` so frontmatter-setting plugins run before this one and are not later overwritten.
- If frontmatter is optional, default it to `{}` instead of null.
Example fix
// before — plugin that breaks frontmatter
export function remarkPlugin() {
return (tree, file) => {
file.data.astro.frontmatter = null;
};
}
// after
export function remarkPlugin() {
return (tree, file) => {
file.data.astro.frontmatter = { ...(file.data.astro.frontmatter ?? {}), ...computed };
};
} Defensive patterns
Strategy: validation
Validate before calling
function isFrontmatterValid(fm: unknown): boolean {
return fm !== null && fm !== undefined && typeof fm === 'object' && !Array.isArray(fm);
}
// in plugin:
if (!isFrontmatterValid(file.data.astro?.frontmatter)) {
file.data.astro = { ...(file.data.astro ?? {}), frontmatter: {} };
} Type guard
function isValidFrontmatter(fm: unknown): fm is Record<string, unknown> {
return fm !== null && typeof fm === 'object' && !Array.isArray(fm);
} Prevention
- Never set `astro.frontmatter` to null/undefined in remark/rehype plugins.
- Default frontmatter to `{}` when conditionally cleared.
- Verify plugin order so frontmatter is set before MDX export emission.
When it happens
Trigger: A remark/rehype plugin assigns `file.data.astro.frontmatter = null` (or `undefined`, or a circular object). Frontmatter is computed asynchronously and not set by the time this plugin runs. A plugin replaces `file.data.astro` entirely.
Common situations: Custom remark plugin that processes frontmatter and forgets to keep a valid object. MDX frontmatter removed conditionally. Plugin ordering issue where a later plugin nulls frontmatter.
Related errors
- [MDX] A Sätteri plugin attempted to inject invalid frontmatt
- InvalidFrontmatterInjectionError
- NoMatchingImport
- Expected a matching import for component `${tagName}`. Did y
- The markdown processor "${processor.name}" does not provide
AI-assisted analysis of withastro/astro@d081033d5f (2026-08-12).
Data as JSON: /api/errors/606c5d14074d8c3e.
Report an issue: GitHub.