RocketChat/Rocket.Chat · warning · Meteor.Error
Moment locale not found: ${locale}
Error message
Moment locale not found: ${locale} What it means
The `loadLocale` Meteor method returns the Moment.js locale source so clients can format dates. It calls `getMomentLocale(locale)`, which tries the server assets `moment-locales/<locale>.js`, then the primary subtag (`zh-cn` -> `zh`), then a small alias map (`ug`->`ug-cn`, `zh`->`zh-cn`); if none of those asset files exist it throws, and the method re-wraps it as `Moment locale not found: ${locale}` (code `moment-locale-not-found`). Moment locale filenames are lowercase and hyphenated, so codes like `zh_CN` (underscore) or unknown variants fail all three lookups.
Source
Thrown at apps/meteor/server/meteor-methods/platform/loadLocale.ts:21
import { Meteor } from 'meteor/meteor';
import { getMomentLocale } from '../../lib/getMomentLocale';
declare module '@rocket.chat/ddp-client' {
// eslint-disable-next-line @typescript-eslint/naming-convention
interface ServerMethods {
loadLocale(locale: string): string | undefined;
}
}
Meteor.methods<ServerMethods>({
loadLocale(locale) {
check(locale, String);
try {
return getMomentLocale(locale);
} catch (error: any) {
throw new Meteor.Error(error.message, `Moment locale not found: ${locale}`);
}
},
});
View on GitHub (pinned to b2c16d5842)
Solutions
- Normalize the code before calling: lowercase it and replace underscores with hyphens (`'zh_CN'.toLowerCase().replace('_', '-')` -> `zh-cn`)
- Trim to the primary subtag when the full tag is unavailable (`pt-br` -> `pt`), mirroring the server's own fallback order
- Map special cases the server maps (`zh` -> `zh-cn`, `ug` -> `ug-cn`) and check the locale exists client-side via `moment.locales()` before requesting
- On failure, retry once with the workspace default locale (e.g. `en`) so date formatting still works
Example fix
// before
Meteor.call('loadLocale', user.language, (err, src) => { ... });
// after
const normalize = (l) => l.toLowerCase().replace('_', '-');
const primary = (l) => l.split('-').shift();
Meteor.call('loadLocale', normalize(user.language), (err, src) => {
if (err) return Meteor.call('loadLocale', primary(normalize(user.language)) || 'en', cb);
useLocaleSource(src);
}); Defensive patterns
Strategy: validation
Validate before calling
const KNOWN = new Set(['en', 'pt', 'pt-br', 'zh', 'zh-cn', 'ug', 'ug-cn' /* ... */]);
const normalize = (l) => l.toLowerCase().replace('_', '-');
const resolveLocale = (l) => {
const n = normalize(l);
if (KNOWN.has(n)) return n;
const primary = n.split('-').shift();
if (KNOWN.has(primary)) return primary;
return 'en';
};
Meteor.call('loadLocale', resolveLocale(user.language), cb); Type guard
const isKnownMomentLocale = (locale: string): boolean =>
typeof moment !== 'undefined' &&
moment.locales().includes(locale.toLowerCase().replace('_', '-')); Try / catch
Meteor.call('loadLocale', locale, (err, src) => {
if (err) {
// degrade gracefully: retry once with the default locale
return Meteor.call('loadLocale', 'en', fallbackCb);
}
useLocaleSource(src);
}); Prevention
- Always normalize locale strings (lowercase, hyphen-separated) before sending them to the server
- Keep the client's list of supported locales in sync with the server's moment-locales assets
- Never forward raw navigator.language; map it to a moment-supported code first
When it happens
Trigger: Calling `Meteor.call('loadLocale', locale)` with a code that has no matching moment-locales asset: `zh_CN` instead of `zh-cn`, a regional variant that is not bundled (`en-US` has no asset; only the fallback to `en` saves it if `en.js` exists), or a typo like `ptb`. Clients usually forward the user's saved `language` setting or `navigator.language` verbatim.
Common situations: Browser BCP47 tags (en-US, zh-Hant) passed unnormalized; a user language saved by an older Rocket.Chat whose moment bundle differed from the current server's `moment-locales` assets; locale strings with underscores or uppercase letters.
Related errors
- moment-locale-not-found
- FileUpload_Error
- error-action-not-allowed
- error-invalid-message
- error-invalid-room
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/d00e6c0b6117c66f.
Report an issue: GitHub.