marmelab/react-admin · error
The i18nProvider returned a Promise for the messages of the
Error message
The i18nProvider returned a Promise for the messages of the default locale (${initialLocale}). Please update your i18nProvider to return the messages of the default locale in a synchronous way. What it means
ra-i18n-polyglot's polyglotI18nProvider requires getMessages for the initial (default) locale to return messages synchronously, because it must build the Polyglot instance eagerly at setup time. If the function returns a Promise for the default locale, the provider cannot proceed and throws at creation time.
Source
Thrown at packages/ra-i18n-polyglot/src/index.ts:38
* fr: frenchMessages,
* en: englishMessages,
* };
* const i18nProvider = polyglotI18nProvider(
* locale => messages[locale],
* 'en',
* [{ locale: 'en', name: 'English' }, { locale: 'fr', name: 'Français' }]
* )
*/
export default (
getMessages: GetMessages,
initialLocale: string = 'en',
availableLocales: Locale[] | any = [{ locale: 'en', name: 'English' }],
polyglotOptions: any = {}
): I18nProvider => {
let locale = initialLocale;
const messages = getMessages(initialLocale);
if (messages instanceof Promise) {
throw new Error(
`The i18nProvider returned a Promise for the messages of the default locale (${initialLocale}). Please update your i18nProvider to return the messages of the default locale in a synchronous way.`
);
}
let availableLocalesFinal, polyglotOptionsFinal;
if (Array.isArray(availableLocales)) {
// third argument is an array of locales
availableLocalesFinal = availableLocales;
polyglotOptionsFinal = polyglotOptions;
} else {
// third argument is the polyglotOptions
availableLocalesFinal = [{ locale: 'en', name: 'English' }];
polyglotOptionsFinal = availableLocales;
}
const polyglot = new Polyglot({
locale,
phrases: { '': '', ...messages },
...polyglotOptionsFinal,View on GitHub (pinned to 051f511bb0)
Solutions
- Make getMessages return messages synchronously for the default locale (static import or preloaded object)
- Load async locales only for non-default locales, using async/await inside getMessages with a cached synchronous default
- Pre-fetch all messages before creating the provider, then use a synchronous getMessages that reads from the cache
Example fix
// before
polyglotI18nProvider(async locale => {
const messages = await fetch(`/i18n/${locale}.json`).then(r => r.json());
return messages;
}, 'en');
// after
import en from './i18n/en.json';
const cache = { en };
polyglotI18nProvider(locale => {
if (cache[locale]) return cache[locale];
return fetch(`/i18n/${locale}.json`)
.then(r => r.json())
.then(messages => { cache[locale] = messages; return messages; });
}, 'en'); Defensive patterns
Strategy: validation
Validate before calling
const getMessages = locale => {
const messages = cache[locale];
if (messages instanceof Promise) {
throw new Error(`getMessages returned a Promise for default locale: ${locale}`);
}
return messages;
}; Type guard
const isSyncMessages = (m: unknown): m is Record<string, any> =>
!(m instanceof Promise) && typeof m === 'object' && m !== null; Try / catch
try {
const i18nProvider = polyglotI18nProvider(getMessages, 'en', availableLocales);
} catch (e) {
if (e instanceof Error && e.message.includes('returned a Promise for the messages')) {
console.error('Default locale messages must be synchronous; preload translations first');
throw e;
}
throw e;
} Prevention
- Always statically import the default locale messages
- Cache async locale loads and return the default synchronously from a cache
- Never use an async function directly as getMessages for the default locale
- Add a smoke test that constructs the provider at startup
When it happens
Trigger: polyglotI18nProvider(locale => fetch(...)...) with the default locale resolved via an async call; passing an async getMessages function whose first invocation (for initialLocale) is asynchronous; using jsonServerProvider-style async message loading for the startup locale.
Common situations: Migrating from a fully async i18n setup; loading translations from an API for all locales including the initial one; mixing static imports for 'en' with dynamic imports for other locales but forgetting to keep the default synchronous.
Related errors
- useReferenceFieldController: missing reference prop. You mus
- The dataProvider threw an error. It should return a rejected
- useTranslatableContext must be used inside a TranslatableCon
- The dataProvider is not initialized.
- <${displayName}> component is not properly configured, some
AI-assisted analysis of marmelab/react-admin@051f511bb0 (2026-08-30).
Data as JSON: /api/errors/d1fe9544d28e8eb6.
Report an issue: GitHub.