GoogleChrome/lighthouse · error · Error

Unsupported locale '${requestedLocale}'

Error message

Unsupported locale '${requestedLocale}'

What it means

Thrown by swapLocale when the requested locale is not registered/available according to format.hasLocale. swapLocale re-renders an existing LHR into a different locale and refuses to proceed if it cannot look up the locale's messages. (A separate later guard also throws if the LHR is missing icuMessagePaths.)

Source

Thrown at shared/localization/swap-locale.js:52

      "audits[mainthread-work-breakdown].details.headings[1].text",
      "audits[network-rtt].details.headings[1].text",
      "audits[network-server-latency].details.headings[1].text"
    ],
    ...
 */

/**
 * Returns a new LHR with all strings changed to the new `requestedLocale`.
 * @param {LH.Result} lhr
 * @param {LH.Locale} requestedLocale
 * @return {{lhr: LH.Result, missingIcuMessageIds: string[]}}
 */
function swapLocale(lhr, requestedLocale) {
  // Copy LHR to avoid mutating provided LHR.
  lhr = JSON.parse(JSON.stringify(lhr));

  if (!format.hasLocale(requestedLocale)) {
    throw new Error(`Unsupported locale '${requestedLocale}'`);
  }
  const originalLocale = lhr.configSettings.locale;
  const {icuMessagePaths} = lhr.i18n;
  const missingIcuMessageIds = [];

  if (!icuMessagePaths) throw new Error('missing icuMessagePaths');

  for (const [i18nId, icuMessagePath] of Object.entries(icuMessagePaths)) {
    for (const instance of icuMessagePath) {
      // The path that _formatPathAsString() generated.
      let path;
      let values;
      if (typeof instance === 'string') {
        path = instance;
      } else {
        path = instance.path;
        // `values` are the string template values to be used. eg. `values: {wastedBytes: 9028}`
        values = instance.values;

View on GitHub (pinned to 9515cd4e58)

Solutions

  1. Register the locale before swapping: fetch the locale JSON and call format.registerLocaleData(requestedLocale, messages).
  2. Gate the UI/locale picker on format.hasLocale so only registered locales are selectable.
  3. Fall back to the original/default locale when the requested one cannot be loaded.

Example fix

// before
const { lhr } = swapLocale(originalLhr, requestedLocale); // may throw

// after
if (format.hasLocale(requestedLocale)) {
  const { lhr } = swapLocale(originalLhr, requestedLocale);
} else {
  await registerLocaleFor(requestedLocale); // loads + registerLocaleData
}
Defensive patterns

Strategy: validation

Validate before calling

if (!format.hasLocale(requestedLocale)) {
  await ensureLocaleRegistered(requestedLocale); // fetch + registerLocaleData
}
const {lhr} = swapLocale(originalLhr, requestedLocale);

Type guard

const canSwap = (/** @type {LH.Locale} */ loc) => format.hasLocale(loc);

Try / catch

try {
  swapLocale(lhr, requestedLocale);
} catch (err) {
  if (err.message.startsWith('Unsupported locale')) {
    // fall back to original locale
  } else if (err.message === 'missing icuMessagePaths') {
    // LHR has no i18n paths; cannot swap
  } else throw err;
}

Prevention

When it happens

Trigger: Calling swapLocale(lhr, requestedLocale) where format.hasLocale(requestedLocale) is false — i.e. the locale messages were never registered via format.registerLocaleData.

Common situations: Viewer flow where fetching the locale's JSON failed silently or the locale file was not bundled; passing a locale code that Lighthouse does not ship; locale data not registered before swapLocale runs.

Related errors


AI-assisted analysis of GoogleChrome/lighthouse@9515cd4e58 (2026-08-13). Data as JSON: /api/errors/df510c79161089ca. Report an issue: GitHub.