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
- In your plugin, only assign a plain object — skip the assignment entirely when there is nothing to inject
- When merging, spread into the existing value: file.data.astro.frontmatter = { ...file.data.astro.frontmatter, ...newData }
- 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
- Only assign plain JSON objects to data.astro.frontmatter — never null, undefined, arrays, or primitives
- Skip the assignment when there is nothing to inject instead of assigning a sentinel value
- Run markdown builds in CI so a bad plugin fails the pipeline before deploy
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
- not applied.
- not applied.
- Failed to parse image reference
- [ ] Astro now supports MDX! Support for components in ".md"…
- InvalidContentEntrySlugError
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)