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

  1. Clear the site's localStorage (key `theme` / `docusaurus-color-mode`) and reload to reset to a valid value.
  2. If you swizzled the toggle, validate/sanitize the incoming `colorMode` with `coerceToColorMode` before passing it into the cycle function.
  3. 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

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


AI-assisted analysis of facebook/docusaurus@3f483e80e3 (2026-08-12). Data as JSON: /api/errors/b68d3a4a2c47d6c9. Report an issue: GitHub.