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

  1. 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).
  2. 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.
  3. If plaintext is acceptable, set the fence to ```text (or omit the lang) to silence the warn intentionally.
  4. 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

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


AI-assisted analysis of withastro/astro@52e6c34790 (2026-08-18). Data as JSON: /api/errors/d76e427ce00e63ae. Report an issue: GitHub.