Skip to content

fix: render unknown code fence languages as plain text - #169

Open
rsbh wants to merge 1 commit into
mainfrom
fix/shiki-unknown-language-fallback
Open

fix: render unknown code fence languages as plain text#169
rsbh wants to merge 1 commit into
mainfrom
fix/shiki-unknown-language-fallback

Conversation

@rsbh

@rsbh rsbh commented Aug 7, 2026

Copy link
Copy Markdown
Member

Problem

A code fence using a language Shiki doesn't bundle crashes the page at build time:

Language `logql` not found, you may need to load it first

logql isn't one of the 347 grammars in @shikijs/langs, but the same applies to any unbundled language — including a simple typo like ```typescirpt.

Cause

Shiki's rehype integration only loads a language when it's in the bundle, and otherwise falls through to fallbackLanguage:

if (!isLanguageLoaded(lang)) {
  if (lazy && lang in highlighter.getBundledLanguages()) await highlighter.loadLanguage(lang);
  else if (fallbackLanguage) { lang = fallbackLanguage; ... }
}

fallbackLanguage was unset, so neither branch was taken, the unknown language reached codeToHast, and it threw.

Fix

Set fallbackLanguage: 'text' so unbundled languages render unhighlighted instead of failing the page.

'text' is safe as the fallback value: it's a Shiki special language, so isLanguageLoaded short-circuits and loadLanguage('text') is never called — that call would itself throw, since plain languages aren't in the bundle.

Fumadocs' defaults are spread because the config type is RehypeCodeOptions rather than Partial<RehypeCodeOptions>, and the CodeOptionsThemes union inside requires theme/themes. This is a runtime no-op — the highlighter factory already merges defaults over the passed options.

Verification

Checked against real SSR output from a dev server, on a page exercising each case:

Case Result
```logql plain text, no throw
```definitely-not-a-language plain text
```ts 19/20 spans highlighted
title="example.js" title renders
// [!code highlight] has-highlighted set, marker stripped
bare fence plain text

Dual themes intact (shiki-themes github-light github-dark); dev server log clean. bun run build:cli and biome lint pass; typecheck error count unchanged at 62 pre-existing before and after.

Note

The fallback is silent by design — fallbackLanguage swaps the language without throwing, so onError never fires and there's no hook to log it. A mistyped language now degrades to plain text with no warning. Happy to add a warning in a follow-up if that tradeoff isn't wanted.

🤖 Generated with Claude Code

Code fences using a language Shiki does not bundle threw at build time,
for example "Language `logql` not found, you may need to load it first".

Shiki's rehype integration only loads a language when it is in the bundle,
and otherwise falls through to `fallbackLanguage`. That option was unset,
so the unknown language reached `codeToHast` and threw. Set it to `text`
so those blocks render unhighlighted instead of failing the page.

Fumadocs' defaults are spread because the config type is not `Partial`;
the highlighter factory merges them anyway, so themes, notation
transformers and meta parsing are unaffected.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Aug 7, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
chronicle Ready Ready Preview Aug 7, 2026 4:59am

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 819c0a79-ca96-4ad8-b117-06857bc6e0b3

📥 Commits

Reviewing files that changed from the base of the PR and between d8c82d5 and e91411a.

📒 Files selected for processing (1)
  • packages/chronicle/src/server/vite-config.ts

📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes
    • Improved MDX code block rendering by displaying unsupported languages as plain text instead of causing rendering failures.

Walkthrough

The Vite configuration updates MDX code rendering. It uses the default rehype code options and falls back to text when Shiki does not support a language.

Changes

MDX code rendering

Layer / File(s) Summary
Configure unsupported language fallback
packages/chronicle/src/server/vite-config.ts
Imports rehypeCodeDefaultOptions and configures text as the fallback language for unsupported Shiki code fences.

Estimated code review effort: 1 (Trivial) | ~5 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the primary change: rendering unknown code fence languages as plain text.
Description check ✅ Passed The description explains the problem, cause, fix, verification results, and trade-offs related to the changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/shiki-unknown-language-fallback

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@rsbh
rsbh requested a review from rohilsurana August 7, 2026 05:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant