GoogleChrome/lighthouse · error · Error
could not fetch data for locale: ${locale}
Error message
could not fetch data for locale: ${locale} What it means
Thrown by the viewer's _swapLocale when the freshly fetched locale-messages value is falsy. _fetchLocaleMessages does fetch('./locales/<locale>.json').json(); if that resolves to null/empty (missing or malformed locale file, failed fetch that still parsed), _swapLocale aborts with the offending locale in the message. It runs only after locale data registration is attempted.
Source
Thrown at viewer/app/src/viewer-ui-features.js:90
}
}
/**
* @param {LH.Locale} locale
* @return {Promise<LhlMessages>}
*/
async _fetchLocaleMessages(locale) {
const response = await fetch(`./locales/${locale}.json`);
return response.json();
}
/**
* @param {LH.Locale} locale
*/
async _swapLocale(locale) {
const lhlMessages = await this._fetchLocaleMessages(locale);
const i18nModule = await this._getI18nModule();
if (!lhlMessages) throw new Error(`could not fetch data for locale: ${locale}`);
i18nModule.format.registerLocaleData(locale, lhlMessages);
const newLhr = i18nModule.swapLocale(this.json, locale).lhr;
this._refreshCallback(newLhr);
}
/**
* The i18n module is only need for swap-locale-feature.js, and is ~30KB,
* so it is lazily loaded.
* TODO: reduce the size of the formatting code and include it always (remove lazy load),
* possibly moving into base ReportUIFeatures.
*/
_getI18nModule() {
return import('../../../shared/localization/i18n-module.js');
}
}
View on GitHub (pinned to 9515cd4e58)
Solutions
- Ensure locales/<locale>.json is present and non-empty in the deployed viewer build for every locale offered in the picker.
- Gate the locale picker on availability: verify the locale file is bundled before allowing selection.
- On this error, fall back to the current/default locale instead of leaving the UI broken.
Example fix
// before
async _fetchLocaleMessages(locale) {
const r = await fetch(`./locales/${locale}.json`);
return r.json(); // may be null/empty -> throws in _swapLocale
}
// after
async _fetchLocaleMessages(locale) {
const r = await fetch(`./locales/${locale}.json`);
if (!r.ok) return null;
const msgs = await r.json();
return msgs && Object.keys(msgs).length ? msgs : null;
} Defensive patterns
Strategy: validation
Validate before calling
async _fetchLocaleMessages(locale) {
const r = await fetch(`./locales/${locale}.json`);
if (!r.ok) return null;
const msgs = await r.json();
return msgs && Object.keys(msgs).length ? msgs : null;
}
// Then only proceed in _swapLocale when the result is non-null. Type guard
/** @param {unknown} m */
function hasLocaleMessages(m) {
return m != null && typeof m === 'object' && Object.keys(/** @type {object} */ (m)).length > 0;
} Try / catch
try {
await this._swapLocale(locale);
} catch (err) {
if (err.message.startsWith('could not fetch data for locale')) {
showMessage(`Locale '${locale}' is unavailable; staying on the current language.`);
} else throw err;
} Prevention
- Bundle locales/<locale>.json for every locale offered in the viewer's picker.
- Verify fetch response.ok and that the parsed body is non-empty before registering.
- Fall back to the current/default locale when a locale file is unavailable.
When it happens
Trigger: Selecting a locale in the viewer whose locales/<locale>.json file is missing from the build, returns an empty body, or resolves to null/empty — then _swapLocale receives no usable messages.
Common situations: Viewer build that did not bundle the requested locale file; locale filename mismatch (case/region code); served locales directory incomplete; CDN/path misconfiguration so the file 404s but parsing yields a falsy result.
Related errors
- Unsupported locale '${locale}'
- Unsupported locale '${requestedLocale}'
- expected locale file to exist: ${traceEnginePath}
- Invalid value: Argument 'locale' must be a string
- Invalid value: Argument 'extra-headers' must be a string
AI-assisted analysis of GoogleChrome/lighthouse@9515cd4e58 (2026-08-13).
Data as JSON: /api/errors/bae8156b83d21222.
Report an issue: GitHub.