prettier/prettier · error · UnexpectedNodeError
Unexpected Markdown node type: ${JSON.stringify(node.type)}.
Error message
Unexpected Markdown node type: ${JSON.stringify(node.type)}. What it means
Thrown by the markdown (mdast) printer's switch default (src/language-markdown/print/mdast.js:424) via `UnexpectedNodeError(node, "Markdown")`. It also covers `frontMatter` (handled in core), `tableRow` (handled in `table`), and `listItem` (handled in `list`). Any mdast node type not in the handled list raises this. Marked `/* c8 ignore next */`.
Source
Thrown at src/language-markdown/print/mdast.js:424
"$$",
node.meta ? " " + node.meta : "",
hardline,
node.value ? [replaceEndOfLine(node.value, hardline), hardline] : "",
"$$",
];
case "inlineMath":
// remark-math trims content but we don't want to remove whitespaces
// since it's very possible that it's recognized as math accidentally
return options.originalText.slice(locStart(node), locEnd(node));
case "text":
return replaceEndOfLine(node.value, hardline);
case "frontMatter": // Handled in core
case "tableRow": // handled in "table"
case "listItem": // handled in "list"
default:
/* c8 ignore next */
throw new UnexpectedNodeError(node, "Markdown");
}
}
function printRoot(path, options, print) {
/** @typedef {{ index: number, offset: number }} IgnorePosition */
/** @type {Array<{start: IgnorePosition, end: IgnorePosition}>} */
const ignoreRanges = [];
/** @type {IgnorePosition | null} */
let ignoreStart = null;
const { children } = path.node;
for (const [index, childNode] of children.entries()) {
switch (isPrettierIgnore(childNode)) {
case "start":
if (ignoreStart === null) {
ignoreStart = { index, offset: childNode.position.end.offset };
}View on GitHub (pinned to 903845c7d1)
Solutions
- Check `error.node.type` to identify the unrecognized markdown node.
- Add a Prettier plugin/printer case that handles the custom mdast node type, or remove the remark plugin that emits it.
- Align remark and Prettier versions so node vocabularies match.
- Strip/transform custom nodes before formatting if you don't need them printed.
Example fix
// before
// remark plugin injects { type: 'callout', ... } which printer lacks
await prettier.format(md, { parser: 'markdown', plugins: [remarkCallouts] });
// after — drop the plugin or add a printer for 'callout'
await prettier.format(md, { parser: 'markdown' }); Defensive patterns
Strategy: try-catch
Validate before calling
const MDAST_OK = new Set(['root','paragraph','heading','code','html','list','listItem','blockquote','thematicBreak','link','linkReference','image','imageReference','footnoteDefinition','footnoteReference','table','tableCell','text','emphasis','strong','delete','inlineCode','break','jsx','esComment','math','inlineMath','frontMatter','tableRow']);
function findUnknownMdast(ast) {
let bad;
walk(ast, (n) => { if (n.type && !MDAST_OK.has(n.type)) bad = n.type; });
return bad;
} Type guard
function isKnownMdastType(type) {
return ['root','paragraph','heading','code','html','list','listItem','blockquote','thematicBreak','link','linkReference','image','imageReference','table','text','emphasis','strong','inlineCode','break','jsx','esComment','math','inlineMath'].includes(type);
} Try / catch
try {
await prettier.format(md, { parser: 'markdown', plugins });
} catch (e) {
if (/Unexpected Markdown node type/.test(e.message)) {
return prettier.format(md, { parser: 'markdown' }); // drop custom remark plugins
}
throw e;
} Prevention
- Pair custom remark plugins with a Prettier printer for their node types.
- Don't mix remark plugin versions that rename mdast nodes.
- Test markdown formatting with the plugins you ship.
When it happens
Trigger: A markdown AST (mdast) node whose `type` is not recognized — e.g. a remark plugin injecting custom node types (callouts, directives, math variants) without a corresponding Prettier printer case, or a hand-built mdast tree passed to the printer.
Common situations: Using remark/mdx plugins that add custom mdast node types (remark-directive, remark-callouts) without a Prettier plugin that prints them; remark version producing node names newer Prettier doesn't know.
Related errors
- Unexpected JSX node type: ${JSON.stringify(node.type)}.
- Unexpected specifier node type: ${JSON.stringify(node.type)}
- Unexpected TypeScript node type: ${JSON.stringify(node.type)
- Unexpected JSON node type: ${JSON.stringify(node.type)}.
- Unexpected YAML node type: ${JSON.stringify(node.type)}.
AI-assisted analysis of prettier/prettier@903845c7d1 (2026-08-09).
Data as JSON: /api/errors/4dc6d76adedfef02.
Report an issue: GitHub.