withastro/astro · warning
[Shiki] The language ${langStr} doesn't exist, falling back
Error message
[Shiki] The language ${langStr} doesn't exist, falling back to "plaintext". What it means
Astro's shared Shiki highlighter helper (used for Markdown code fences and the `<Code />` component) resolves language aliases, then tries `highlighter.loadLanguage()` when the language isn't already loaded (packages/internal-helpers/src/shiki.ts:198). If loading throws — the id isn't a bundled Shiki language — it prints this console.warn and highlights the block as plaintext instead. Nothing fails; the code block still renders, just without tokens. The message distinguishes aliases, so `"ts" (aliased to "typescript")`-style output tells you exactly which id failed.
Source
Thrown at packages/internal-helpers/src/shiki.ts:198
engine: shikiEngine,
});
async function highlight(
code: string,
lang = 'plaintext',
options: ShikiHighlighterHighlightOptions,
to: 'hast' | 'html',
) {
const resolvedLang = langAlias[lang] ?? lang;
const loadedLanguages = highlighter.getLoadedLanguages();
if (!isSpecialLang(lang) && !loadedLanguages.includes(resolvedLang)) {
try {
await highlighter.loadLanguage(resolvedLang as BundledLanguage);
} catch (_err) {
const langStr =
lang === resolvedLang ? `"${lang}"` : `"${lang}" (aliased to "${resolvedLang}")`;
console.warn(`[Shiki] The language ${langStr} doesn't exist, falling back to "plaintext".`);
lang = 'plaintext';
}
}
code = code.replace(/(?:\r\n|\r|\n)$/, '');
const themeOptions = Object.values(themes).length ? { themes } : { theme };
const inline = options?.inline ?? false;
return highlighter[to === 'html' ? 'codeToHtml' : 'codeToHast'](code, {
...themeOptions,
defaultColor: options.defaultColor,
lang,
// NOTE: while we can spread `options.attributes` here so that Shiki can auto-serialize this as rendered
// attributes on the top-level tag, it's not clear whether it is fine to pass all attributes as meta, as
// they're technically neither meta nor parsed from Shiki's `parseMetaString` API.
meta: options?.meta ? { __raw: options?.meta } : undefined,
transformers: [View on GitHub (pinned to 52e6c34790)
Solutions
- Correct the language id to one Shiki bundles — check the id against Shiki's bundled-languages list (e.g. 'typescript', 'tsx', 'python'), or the alias you assumed (e.g. 'js'→javascript works, 'javacript' does not).
- If you need a non-bundled language, register a TextMate grammar under `markdown.shikiConfig.langs` in astro.config and reference the fence by that registered id.
- If plaintext is acceptable, set the fence to ```text (or omit the lang) to silence the warn intentionally.
- If warnings come from third-party Markdown you don't control, wrap code blocks with `<Code>` after normalizing lang, or filter fences in a rehype plugin before they reach Shiki.
Example fix
```md <!-- before --> ```haskell2 main = putStrLn "hi" ``` <!-- after --> ```haskell main = putStrLn "hi" ``` ```
Defensive patterns
Strategy: validation
Validate before calling
// validate fence languages against Shiki's bundled set before highlighting
import { bundledLanguages, codeToHtml } from 'shiki';
const isKnownLang = (lang: string) =>
lang === 'text' || lang === 'plaintext' || lang === 'html' || lang in bundledLanguages;
const lang = 'tsx';
if (!isKnownLang(lang)) {
console.warn(`Skipping highlight for unknown language: ${lang}`);
} Type guard
import type { BundledLanguage } from 'shiki';
import { bundledLanguages } from 'shiki';
const isBundledLanguage = (l: string): l is BundledLanguage => l in bundledLanguages; Prevention
- Fence code blocks with exact Shiki ids/aliases (typescript, ts, py, bash) — check the Shiki languages reference when unsure rather than guessing.
- For custom languages, register the TextMate grammar in `markdown.shikiConfig.langs` and fence by the id you registered, keeping content-source and highlighter config in sync.
- If content comes from users or CMS data, normalize/whitelist `lang` before it reaches Markdown so one bad fence doesn't spray warnings across every dev build.
- Use `text` fences deliberately for output/console samples instead of guessing ids like 'console' or 'shell'.
When it happens
Trigger: Passing a `lang` that is neither a special lang (plaintext/text/html) nor a key/alias in Shiki's bundled languages: a Markdown fence like ```foo, `<Code lang="jsx-like">`, or a custom grammar id that was never registered via `markdown.shikiConfig.langs`. It also fires when a language was excluded from the highlighter bundle (shikiConfig `langs` narrowed to a subset) but appears in a code fence.
Common situations: Typos in fence languages (`js` vs `jss`, `hs` vs `haskell`); using an unofficial language id from another highlighter (Prism aliases like `cxx` or `node`); fence tags that aren't languages at all (```title=..., or a stray ```console when the intended language differs); registering custom TextMate grammars in shikiConfig but referencing them by file path instead of the registered id.
Related errors
- Unable to load the language: ${lang}
- Shiki syntax highlighting uses inline styles that are not co
- ${colors.bold(plugin)} not applied.
- To inherit Markdown plugins in MDX, please use explicit impo
- `markdown.remarkPlugins`, `markdown.rehypePlugins`, and `mar
AI-assisted analysis of withastro/astro@52e6c34790 (2026-08-18).
Data as JSON: /api/errors/d76e427ce00e63ae.
Report an issue: GitHub.