dotnet/runtime · error · Error

Locale or culture name is null or empty. localeName=${locale

Error message

Locale or culture name is null or empty. localeName=${localeName}, cultureName=${cultureName}

What it means

SystemJS_GetLocaleInfo normalizes both the locale and culture strings via Intl.getCanonicalLocales; if either resolves to undefined (unparseable, non-canonical, or empty after normalization) it throws this. The surrounding try/catch catches it, sets dstLength to -1, and marshals the message back to .NET as a locale-resolution failure rather than crashing the runtime.

Source

Thrown at src/native/libs/System.Native.Browser/native/globalization-locale.ts:23

import { _ems_ } from "../../Common/JavaScript/ems-ambient";

// char16_t* SystemJS_GetLocaleInfo (const uint16_t* locale, int32_t localeLength, const uint16_t* culture, int32_t cultureLength, const uint16_t* result, int32_t resultMaxLength, int *resultLength);
export function SystemJS_GetLocaleInfo(culture: number, cultureLength: number, locale: number, localeLength: number, dst: number, dstMaxLength: number, dstLength: Int32Ptr): VoidPtr {
    const OUTER_SEPARATOR = "##";
    try {
        const localeNameOriginal = _ems_.dotnetBrowserUtilsExports.utf16ToString(<any>locale, <any>(locale + 2 * localeLength));
        const localeName = normalizeLocale(localeNameOriginal);
        if (!localeName && localeNameOriginal) {
            // handle non-standard or malformed locales by forwarding the locale code
            _ems_.dotnetBrowserUtilsExports.stringToUTF16(dst, dst + 2 * localeNameOriginal.length, localeNameOriginal);
            _ems_.dotnetApi.setHeapI32(dstLength, localeNameOriginal.length);
            return 0 as any;
        }
        const cultureNameOriginal = _ems_.dotnetBrowserUtilsExports.utf16ToString(<any>culture, <any>(culture + 2 * cultureLength));
        const cultureName = normalizeLocale(cultureNameOriginal);

        if (!localeName || !cultureName)
            throw new Error(`Locale or culture name is null or empty. localeName=${localeName}, cultureName=${cultureName}`);

        const localeParts = localeName.split("-");
        // cultureName can be in a form of:
        // 1) "language", e.g. "zh"
        // 2) "language-region", e.g. "zn-CN"
        // 3) "language-script-region", e.g. "zh-Hans-CN"
        // 4) "language-script", e.g. "zh-Hans" (served in the catch block below)
        let languageName, regionName;
        try {
            const region = localeParts.length > 1 ? localeParts.pop() : undefined;
            // this line might fail if form 4 from the comment above is used:
            regionName = region ? new Intl.DisplayNames([cultureName], { type: "region" }).of(region) : undefined;
            const language = localeParts.join("-");
            languageName = new Intl.DisplayNames([cultureName], { type: "language" }).of(language);
        } catch (error) {
            if (error instanceof RangeError) {
                // if it failed from this reason then cultureName is in a form "language-script", without region
                try {

View on GitHub (pinned to 290d5ab72c)

Solutions

  1. Set an explicit, valid culture in the .NET app (e.g. Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US")) before calling locale/globalization APIs.
  2. Ensure the browser/Node host has full ICU data (Node built with --with-intl=full-icu, not the default small-icu) so getCanonicalLocales and DisplayNames resolve.
  3. Sanitize user-supplied locale strings to a known-good fallback before they reach globalization.

Example fix

// before
var ci = new CultureInfo(""); // empty culture -> null/empty locale -> throw

// after
var ci = new CultureInfo("en-US");
Defensive patterns

Strategy: validation

Validate before calling

function isValidLocale(s) {
  if (!s) return false;
  try { return Intl.getCanonicalLocales(s).length > 0; }
  catch { return false; }
}
if (!isValidLocale(locale) || !isValidLocale(culture)) useFallbackCulture();

Try / catch

// .NET caller: GetLocaleInfo sets *dstLength = -1 on failure; check the returned length
int len = SystemJS_GetLocaleInfo(...);
if (len < 0) { /* marshaled error string; fall back to invariant culture */ }

Prevention

When it happens

Trigger: Passing a locale or culture string to the native locale-info call that Intl.getCanonicalLocales rejects or trims to empty — e.g. " ", "INVALID", an uncanonical "xx-YY", or a culture that normalizeLocale maps to undefined.

Common situations: A .NET WASM app whose host supplies an empty CurrentCulture/CurrentUICulture (server/SSR environment without LANG/UCOM culture), a user-supplied culture textbox, or a host running with reduced ICU data where getCanonicalLocales fails on valid-looking tags.

Related errors


AI-assisted analysis of dotnet/runtime@290d5ab72c (2026-08-06). Data as JSON: /api/errors/a8d98c28ea12a30a. Report an issue: GitHub.