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
- In your plugin, always assign a plain object: `file.data.astro.frontmatter = { ...file.data.astro.frontmatter, myKey: value }`.
- Ensure the injecting plugin runs before rehypeApplyFrontmatterExport (correct remarkPlugins/rehypePlugins order).
- Keep values JSON-serializable (no functions, Dates are fine but Class instances are not).
- 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
- In frontmatter-injecting plugins, merge into a fresh object rather than replacing with arbitrary YAML/JSON parses.
- Order the injecting plugin before built-in rehype plugins that consume frontmatter.
- Unit-test custom plugins against an MDX fixture so invalid shapes fail in tests, not builds.
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
- on `mdx( )` deprecated. Pass to `unified( )` from…
- [ ] Astro now supports MDX! Support for components in ".md"…
- [MDX] A Sätteri plugin attempted to inject invalid…
- [@astrojs/mdx] Importing `getContainerRenderer` from…
- CannotDetermineWeightAndStyleFromFontFile
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)