{"record":{"id":"a8d98c28ea12a30a","repo":"dotnet/runtime","slug":"locale-or-culture-name-is-null-or-empty-localenam-a8d98c","errorCode":null,"errorMessage":"Locale or culture name is null or empty. localeName=${localeName}, cultureName=${cultureName}","messagePattern":"Locale or culture name is null or empty\\. localeName=(.+?), cultureName=(.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/native/libs/System.Native.Browser/native/globalization-locale.ts","lineNumber":23,"sourceCode":"import { _ems_ } from \"../../Common/JavaScript/ems-ambient\";\n\n// 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);\nexport function SystemJS_GetLocaleInfo(culture: number, cultureLength: number, locale: number, localeLength: number, dst: number, dstMaxLength: number, dstLength: Int32Ptr): VoidPtr {\n    const OUTER_SEPARATOR = \"##\";\n    try {\n        const localeNameOriginal = _ems_.dotnetBrowserUtilsExports.utf16ToString(<any>locale, <any>(locale + 2 * localeLength));\n        const localeName = normalizeLocale(localeNameOriginal);\n        if (!localeName && localeNameOriginal) {\n            // handle non-standard or malformed locales by forwarding the locale code\n            _ems_.dotnetBrowserUtilsExports.stringToUTF16(dst, dst + 2 * localeNameOriginal.length, localeNameOriginal);\n            _ems_.dotnetApi.setHeapI32(dstLength, localeNameOriginal.length);\n            return 0 as any;\n        }\n        const cultureNameOriginal = _ems_.dotnetBrowserUtilsExports.utf16ToString(<any>culture, <any>(culture + 2 * cultureLength));\n        const cultureName = normalizeLocale(cultureNameOriginal);\n\n        if (!localeName || !cultureName)\n            throw new Error(`Locale or culture name is null or empty. localeName=${localeName}, cultureName=${cultureName}`);\n\n        const localeParts = localeName.split(\"-\");\n        // cultureName can be in a form of:\n        // 1) \"language\", e.g. \"zh\"\n        // 2) \"language-region\", e.g. \"zn-CN\"\n        // 3) \"language-script-region\", e.g. \"zh-Hans-CN\"\n        // 4) \"language-script\", e.g. \"zh-Hans\" (served in the catch block below)\n        let languageName, regionName;\n        try {\n            const region = localeParts.length > 1 ? localeParts.pop() : undefined;\n            // this line might fail if form 4 from the comment above is used:\n            regionName = region ? new Intl.DisplayNames([cultureName], { type: \"region\" }).of(region) : undefined;\n            const language = localeParts.join(\"-\");\n            languageName = new Intl.DisplayNames([cultureName], { type: \"language\" }).of(language);\n        } catch (error) {\n            if (error instanceof RangeError) {\n                // if it failed from this reason then cultureName is in a form \"language-script\", without region\n                try {","sourceCodeStart":5,"sourceCodeEnd":41,"githubUrl":"https://github.com/dotnet/runtime/blob/290d5ab72cc1102813fbc0406fb186ceadabc340/src/native/libs/System.Native.Browser/native/globalization-locale.ts#L5-L41","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Set an explicit, valid culture in the .NET app (e.g. Thread.CurrentThread.CurrentCulture = new CultureInfo(\"en-US\")) before calling locale/globalization APIs.","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.","Sanitize user-supplied locale strings to a known-good fallback before they reach globalization."],"exampleFix":"// before\nvar ci = new CultureInfo(\"\"); // empty culture -> null/empty locale -> throw\n\n// after\nvar ci = new CultureInfo(\"en-US\");","handlingStrategy":"validation","validationCode":"function isValidLocale(s) {\n  if (!s) return false;\n  try { return Intl.getCanonicalLocales(s).length > 0; }\n  catch { return false; }\n}\nif (!isValidLocale(locale) || !isValidLocale(culture)) useFallbackCulture();","typeGuard":null,"tryCatchPattern":"// .NET caller: GetLocaleInfo sets *dstLength = -1 on failure; check the returned length\nint len = SystemJS_GetLocaleInfo(...);\nif (len < 0) { /* marshaled error string; fall back to invariant culture */ }","preventionTips":["Default to a concrete culture like en-US rather than relying on host-detected empty cultures.","Bundle full ICU data in Node/WASM hosts used for globalization.","Sanitize user-supplied locale strings to a known-good value before they reach the runtime."],"tags":["globalization","locale","icu","intl"],"analyzedSha":"290d5ab72cc1102813fbc0406fb186ceadabc340","analyzedAt":"2026-08-06T19:57:01.276Z","schemaVersion":2},"datasetVersion":"2026-08-06T23:17:07.152Z"}