[Bug] LaTeX math corrupted by markdown escaping and not rendered in terminal output
Bug Description
Title: Terminal corrupts and does not render LaTeX math in assistant output
Environment: Claude Code CLI, Windows 11, PowerShell.
Summary: Assistant messages containing LaTeX math are (1) shown as raw source instead of rendered, and (2) silently corrupted by GitHub-flavored-markdown backslash-escaping applied before display, which mangles LaTeX spacing macros.
Repro & evidence:
- Backslash-punctuation escaping (content corruption). The model emits $\forall\, \varepsilon$. It is displayed as $\forall, \varepsilon$ — the \, (LaTeX thin-space) has its backslash stripped, leaving a bare comma. Same failure for \;→;, \:→:, \!→!. Meanwhile \varepsilon, \forall, \ge keep their backslashes, because markdown only escapes ASCII punctuation, not letters. So the escaping is selective and corrupts exactly the spacing macros.
- No math rendering. The model emits $$f(n) = 2n, \qquad g(n) = n.$$. It is displayed verbatim as raw source — $$, \qquad and all — with no math typesetting.
Expected: Either render $...$ / $$...$$ as math, or at minimum pass their contents through verbatim (no backslash-escaping) so the source can be copied into a LaTeX renderer intact.
Actual: Math is not rendered, and backslash-punctuation escapes inside math spans are stripped, silently corrupting the LaTeX. The model emits correct source and cannot see the corruption; the user gets stray punctuation.
Impact: Math-heavy responses are hard to read in-terminal, and — worse — copy-pasting the raw source into a real LaTeX engine yields wrong output wherever spacing macros were used, with no visible warning.
Suggested fix: Treat $...$ / $$...$$ as math spans: skip markdown backslash-escaping inside them, and ideally render them (or fall back to a Unicode/plaintext math presentation).
Environment Info
- Platform: win32
- Terminal: WezTerm
- Version: 2.1.218
- Feedback ID: f64b4b12-cf46-49b0-be57-0edbff64731e
Errors
[]
3 Comments
I built an opt-in
MessageDisplayplugin that preserves Markdown-sensitive LaTeX escapes in Claude Code terminal output:It preserves commands such as
\,,\;,\:,\!, and\\in dollar-delimited LaTeX, so the source remains copyable. The transformation is display-only: it does not modify the transcript or model context, and it makes no network requests.I verified the workaround end-to-end with Claude Code 2.1.220 on macOS/Kitty. The automated test suite also runs on macOS, Linux, and Windows with Git Bash. I have submitted the plugin to the Claude Code community marketplace for review.
This is a source-preservation workaround, not visual equation rendering; a built-in rendering or source-preservation fix would still be preferable. Feedback from a Windows/WezTerm setup would be especially valuable.
While implementing this workaround, I found that the bundled Hook Development skill did not cover the
MessageDisplayevent or its streaming semantics. I opened a separate documentation PR to address that gap: https://github.com/anthropics/claude-code/pull/83374Follow-up: I've since published mathshow, which takes a different angle on the same pain. The
MessageDisplayplugin here preserves LaTeX-sensitive escapes in Claude Code's terminal output — still valuable for copy/paste fidelity. mathshow is agent-agnostic: when the user should see a formula, the agent runs:→ KaTeX preview in the browser. Works with Cursor, Claude Code, Codex, and other agents via the Agent Skills format.
https://github.com/iCodeCraft/mathshow Still not a substitute for fixing markdown escaping at the TUI layer (this issue), but it sidesteps the rendering gap entirely. If you're on Windows/WezTerm, I'd love to know whether the plugin + mathshow combo covers your workflow.
Adding evidence from the other side of the pipe. I maintain a terminal that typesets
$…$/$$…$$in command output, so I see exactly which bytes Claude Code writes, twice: once while the answer streams, and once when the finished answer is redrawn through the markdown renderer. The streamed bytes are intact; the final redraw is where the LaTeX is changed. Beyond the spacing macros in the report above, three more rules bite:\\→\. Every row separator insidepmatrix/aligned/casesloses one backslash, so a matrix that was valid a second earlier ends up with a stray\at each line end and cannot be parsed by any TeX engine.\[→[and\]→]. A display block delimited with brackets loses both delimiters.=(anA x = bwritten across three lines of matrices) is taken as a setext underline: the paragraph above it becomes an H1 and the=line is deleted from the output. The equation then readsA x b.Minimal reproduction (ask for exactly this, then compare the streamed text with the final redraw):
Final redraw as received by the terminal: the
\\become\, and the=line is gone with the lines above it styled as a heading.Suggested fix, in order of cost: mask
$…$,$$…$$,\(…\)and\[…\]spans before the CommonMark pass and restore them verbatim afterwards (this is what GitHub's renderer does, and what the Codex TUI started doing this week in openai/codex#45501 and openai/codex#45612, with a verbatim fallback for anything it cannot render). Rendering the math is optional; not altering it is the part that matters, because the bytes in the terminal are what gets copied, and what a terminal that can typeset them is given.Observed with Claude Code 2.1.274 on Windows 11, in Folio (the terminal I maintain); the bytes are the same in any terminal, since the change happens before they are written.