facebook/docusaurus · error · Error
unexpected color mode ${colorMode}
Error message
unexpected color mode ${colorMode} What it means
Thrown by `ColorModeToggle`'s 3-value cycle helper when `colorMode` is not one of `null`, `'light'`, or `'dark'`. The function cycles the toggle button through system → light → dark → system, so any other value is a programming error. `ColorMode` is typed as `'light' | 'dark'` and the third state `null` represents 'follow system'.
Source
Thrown at packages/docusaurus-theme-classic/src/theme/ColorModeToggle/index.tsx:39
function getNextColorMode(
colorMode: ColorMode | null,
respectPrefersColorScheme: boolean,
) {
// 2-value transition
if (!respectPrefersColorScheme) {
return colorMode === 'dark' ? 'light' : 'dark';
}
// 3-value transition
switch (colorMode) {
case null:
return 'light';
case 'light':
return 'dark';
case 'dark':
return null;
default:
throw new Error(`unexpected color mode ${colorMode}`);
}
}
function getColorModeLabel(colorMode: ColorMode | null): string {
switch (colorMode) {
case null:
return translate({
message: 'system mode',
id: 'theme.colorToggle.ariaLabel.mode.system',
description: 'The name for the system color mode',
});
case 'light':
return translate({
message: 'light mode',
id: 'theme.colorToggle.ariaLabel.mode.light',
description: 'The name for the light color mode',
});
case 'dark':View on GitHub (pinned to 3f483e80e3)
Solutions
- Clear the site's localStorage (key `theme` / `docusaurus-color-mode`) and reload to reset to a valid value.
- If you swizzled the toggle, validate/sanitize the incoming `colorMode` with `coerceToColorMode` before passing it into the cycle function.
- Ensure no custom code writes arbitrary strings into the color-mode storage key.
Example fix
// before
const next = cycleThreeValue(colorMode); // throws on garbage
// after
import {coerceToColorMode} from '@docusaurus/ThemeCommon';
const safe = coerceToColorMode(rawColorMode); // 'light' | 'dark'
const next = cycleThreeValue(safe); Defensive patterns
Strategy: validation
Validate before calling
import {coerceToColorMode} from '@docusaurus/ThemeCommon';
// coerceToColorMode normalizes arbitrary input to 'light'|'dark'
const safe = coerceToColorMode(rawStored);
const next = cycleThreeValue(safe === lastLightOrDark ? null : safe); // only feed valid values Type guard
type ColorMode = 'light' | 'dark';
function isColorMode(v: unknown): v is ColorMode {
return v === 'light' || v === 'dark';
} Prevention
- Never write arbitrary strings to the color-mode localStorage key.
- Sanitize persisted state on read with `coerceToColorMode`.
- Keep swizzled toggle logic identical to upstream's three-state switch.
When it happens
Trigger: The cycle function receives a `colorMode` that has been corrupted (e.g. an arbitrary string from a malformed `localStorage` entry, a browser extension, or a hand-edited persisted state). It can also fire if a swizzled toggle passes through an unvalidated value.
Common situations: A user has manually edited `theme-color-scheme`/localStorage entries; a browser extension injects a value; a swizzled `ColorModeToggle` reads from a non-standard source; a stale persisted state from an older Docusaurus version survives an upgrade.
Related errors
- unknown item type ${JSON.stringify(item)}
- Wrong icon: ${icon}
- Hook is called outside the <ColorModeProvider>. Please see h
- Docusaurus plugin global data not found for "${pluginName}"
- Docusaurus plugin global data not found for "${pluginName}"
AI-assisted analysis of facebook/docusaurus@3f483e80e3 (2026-08-12).
Data as JSON: /api/errors/b68d3a4a2c47d6c9.
Report an issue: GitHub.