In my hugo papermod powered website where math is rendered by Katex, math blocks were silently failing to render mid-page. Everything after a certain point appeared as raw LaTeX. Here’s the fix.
NOTE: The fix is generated by Claude. It works. But I’m not sure if the mechanism is 100% correct. But it works.
Root Causes
1. Goldmark mangles math before KaTeX sees it
Hugo uses Goldmark as its Markdown renderer. Goldmark processes the .md file first — and it escapes or corrupts characters like \, _, {, } inside math delimiters. By the time KaTeX receives the string, the LaTeX is broken.
This is why \begin{align*} or \Sigma_{XX} would silently fail: the backslashes and braces were being eaten.
Fix: Enable the Goldmark passthrough extension in config.yaml. This tells Goldmark to leave content inside $...$ and $$...$$ completely untouched.
markup:
goldmark:
extensions:
passthrough:
enable: true
delimiters:
block:
- - '$$'
- '$$'
inline:
- - '$'
- '$'
Requires Hugo ≥ v0.122.0.
2. $$ blocks not separated by blank lines
Goldmark only treats a $$ block as display math if it is surrounded by blank lines. Without them, it’s parsed as inline text inside a paragraph, which breaks rendering.
Fix: Ensure every $$ block has a blank line above and below it in the .md source.
3. align* is poorly supported in KaTeX
KaTeX has inconsistent support for standalone \begin{align*}. It works reliably when wrapped inside $$...$$ as \begin{aligned}.
Fix: Replace all \begin{align*} / \end{align*} with \begin{aligned} / \end{aligned}.
# macOS-compatible
perl -i -pe 's/\\begin\{align\*\}/\\begin{aligned}/g; s/\\end\{align\*\}/\\end{aligned}/g' your-post.md
Why One Failure Kills the Rest
KaTeX’s renderMathInElement stops processing subsequent expressions when it hits an error — unless throwOnError: false is set. This explains the “everything after point X breaks” symptom. E.g. put in ./layouts/partials/math.html the following code:
renderMathInElement(document.body, {
delimiters: [
{left: '$$', right: '$$', display: true},
{left: '$', right: '$', display: false},
{left: '\\[', right: '\\]', display: true},
{left: '\\(', right: '\\)', display: false},
],
throwOnError: false
});