{"record":{"id":"8d97e45063271435","repo":"withastro/astro","slug":"invalidfrontmatterinjectionerror","errorCode":"InvalidFrontmatterInjectionError","errorMessage":"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`.","messagePattern":"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`\\.","errorType":"exception","errorClass":"AstroError","httpStatus":null,"severity":"error","filePath":"packages/astro/src/vite-plugin-markdown/index.ts","lineNumber":98,"sourceCode":"\t\t\t\tif (!renderer) {\n\t\t\t\t\tconst { markdown: md, image } = settings.config;\n\t\t\t\t\trenderer = md.processor.createRenderer({\n\t\t\t\t\t\timage,\n\t\t\t\t\t\tsyntaxHighlight: md.syntaxHighlight,\n\t\t\t\t\t\tshikiConfig: md.shikiConfig,\n\t\t\t\t\t\tgfm: md.gfm,\n\t\t\t\t\t\tsmartypants: md.smartypants,\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tconst renderResult = await (await renderer).render(raw.content, {\n\t\t\t\t\tfileURL,\n\t\t\t\t\tfrontmatter: raw.frontmatter,\n\t\t\t\t});\n\n\t\t\t\t// Improve error message for invalid astro frontmatter\n\t\t\t\tif (!isFrontmatterValid(renderResult.metadata.frontmatter)) {\n\t\t\t\t\tthrow new AstroError(AstroErrorData.InvalidFrontmatterInjectionError);\n\t\t\t\t}\n\n\t\t\t\tlet html = renderResult.code;\n\t\t\t\tconst {\n\t\t\t\t\theadings,\n\t\t\t\t\tlocalImagePaths: rawLocalImagePaths,\n\t\t\t\t\tremoteImagePaths,\n\t\t\t\t\tfrontmatter,\n\t\t\t\t} = renderResult.metadata;\n\n\t\t\t\t// Add default charset for markdown pages\n\t\t\t\tconst isMarkdownPage = isPage(fileURL, settings);\n\t\t\t\tconst charset = isMarkdownPage ? '<meta charset=\"utf-8\">' : '';\n\n\t\t\t\t// Resolve all the extracted images from the content\n\t\t\t\tconst localImagePaths: MarkdownImagePath[] = [];\n\t\t\t\tfor (const imagePath of rawLocalImagePaths) {\n\t\t\t\t\tlocalImagePaths.push({","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/withastro/astro/blob/52e6c34790cc8ac4e69e6135ace06049867e5c4a/packages/astro/src/vite-plugin-markdown/index.ts#L80-L116","documentation":"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.","triggerScenarios":"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.","commonSituations":"Custom remark plugins for SEO/reading-time metadata that conditionally skip injection by assigning undefined; porting rehype plugins that treat data as free-form.","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"],"exampleFix":"// before — remark plugin\nexport function myPlugin() {\n\treturn (tree, file) => {\n\t\tfile.data.astro.frontmatter = undefined; // or a failed lookup result\n\t};\n}\n\n// after\nexport function myPlugin() {\n\treturn (tree, file) => {\n\t\tfile.data.astro.frontmatter = { readingTime: 5 }; // plain object only\n\t};\n}","handlingStrategy":"validation","validationCode":"// inside your remark/rehype plugin — validate before injecting\nconst isValidFrontmatter = (fm: unknown): fm is Record<string, unknown> =>\n\tfm !== null && typeof fm === 'object' && !Array.isArray(fm) &&\n\tObject.values(fm).every((v) => typeof v !== 'function');\n\nif (newData && isValidFrontmatter(newData)) {\n\tfile.data.astro.frontmatter = { ...file.data.astro.frontmatter, ...newData };\n}","typeGuard":"const isValidFrontmatter = (fm: unknown): fm is Record<string, unknown> =>\n\tfm !== null && typeof fm === 'object' && !Array.isArray(fm);","tryCatchPattern":null,"preventionTips":["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"],"tags":["markdown","remark","rehype","frontmatter"],"backgroundTag":"markdown-frontmatter-validation","analyzedSha":"52e6c34790cc8ac4e69e6135ace06049867e5c4a","analyzedAt":"2026-08-18T18:48:03.901Z","contentChangedAt":"2026-08-18T18:48:03.901Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}