You will learn how to diagnose HTML output and build logs, then handle common issues like grammars not loading, WASM errors, unrecognized languages, unparsed meta, inline code without highlighting, and conflicts with the CSS theme.

Everyone will run into problems: code without colors, unrecognized languages, or a build failing because of WASM. What distinguishes experienced developers isn't freedom from errors, but the way they diagnose systematically.
Episode 19 covers debugging and troubleshooting strategies: inspecting HTML output, leveraging build logs, then handling the most frequent issues like grammars not loading, WASM errors, unrecognized languages, unparsed meta, inline code not highlighted, and CSS theme conflicts.
The first step of diagnosis is always looking at the rendered result. Run the build, open the page, and inspect the code block elements in DevTools. Watch for three things: whether the element contains token spans, whether attributes like data-line-numbers appear, and whether the colors match the theme.
If the <code> element only contains plain text without spans, rehype-pretty-code never ran highlighting. If spans exist but the colors are wrong, the problem is in the CSS or the theme.
To see the pipeline result without opening a browser, render one small document in Node.js and print its HTML:
node diagnose.mjsimport { unified } from "unified";
import remarkParse from "remark-parse";
import remarkRehype from "remark-rehype";
import rehypePrettyCode from "rehype-pretty-code";
import rehypeStringify from "rehype-stringify";
const markdown = "```ts\nconst a = 1;\n```\n";
const file = await unified()
.use(remarkParse)
.use(remarkRehype)
.use(rehypePrettyCode, { theme: "github-dark-default" })
.use(rehypeStringify)
.process(markdown);
console.log(String(file));This small script prints the raw HTML of one code block. Compare it with your expectation: if the spans are missing, the problem is in the pipeline or plugin configuration.
Shiki hides many warnings by default. Enable logging with the warnings: "warn" option so issues like unrecognized languages appear during the build:
const options = {
theme: "github-dark-default",
warnings: "warn",
};With the warnings option, the build process prints warnings to the terminal instead of silently failing. Apply this in a development environment, then disable it in production once the logs are clean.
Pay attention to the order of build messages: the first message mentioning "language" or "wasm" is usually the main clue. Note the file name and failing line, then compare with the language list registered in the langs option. Small mistakes like a language id typo are often the culprit.
The error "No language registered for X" means grammar X isn't in the langs list in the configuration. The solution is to add that language id:
node -e "import('@shikijs/langs').then(m => console.log(m.langs.some(l => l.id === 'rust')))"If the command prints false, the id you wrote is wrong. Check the complete language list in the @shikijs/langs package to find the correct id.
An error like "Failed to fetch dynamically imported module" on the shiki/wasm file means the WASM file isn't found. Make sure your environment supports dynamic imports for WASM assets, and in Next.js check that output: standalone doesn't exclude the WASM file from the server build.
If you use serverless, avoid loading WASM at runtime; move highlighting to build time with SSG. The JavaScript engine from @shikijs/engine-javascript is an alternative when WASM truly isn't available.
A meta string with no effect is usually caused by a format mistake. Watch the order and quotes: values with spaces must be surrounded by quotes, while features without values like showLineNumbers are written plain. If the attribute doesn't appear in the markup, re-check the meta line on the fenced code block and compare it with the examples in episode 6.
If inline code stays plain, bypassInlineCode probably hasn't been set to false. After setting it, make sure the tokens use a recognized language. Tokens whose color doesn't change could be using an unsupported language or a defaultLang value of plaintext.
The language marker on inline code only works if that language is registered. If the marker uses an unknown id, add that language id to the langs option like with regular code blocks. Consistency between the block and inline language lists eliminates half of this problem.
The highlight is semantically correct but the colors look wrong, for example all tokens are uniformly white or green. This is usually a CSS conflict: a global rule like pre span { color: ... } overrides the token colors. Check DevTools to see which rule overrides color: var(--shiki-dark).
Fix it by targeting more specifically or using an attribute as a distinguisher:
[data-rehype-pretty-code-figure] code span {
color: var(--shiki-dark);
}Higher specificity beats loose global rules. Also make sure keepBackground doesn't create a double background when both the theme and CSS set the background.
Key takeaways:
warnings option surfaces hidden warnings.langs option.In episode 20 you'll learn the latest stable features of Shiki 4 and rehype-pretty-code 0.14: the highlighter API overhaul, the engine option, splitting langs and themes into separate packages, and the inline character highlighting fix in rehype-pretty-code.