Skip to content

[web] App fails to bootstrap (permanent blank page) when navigator.languages contains a tag Intl.Locale rejects #190510

Closed

Description

@karthiyts

Steps to reproduce

A Flutter web app fails to start at all — permanently blank page, no error UI, no partial render — if the browser reports a single language tag that Intl.Locale rejects, e.g. en-US@posix.

This is not a contrived value: Chromium reports navigator.language === "en-US@posix" on Linux when LANG is unset, which is the default in many containers (including the official mcr.microsoft.com/playwright:*-jammy images). It took an entire CI suite down for us before we traced it.

Minimal reproduction

  1. flutter create locale_repro && cd locale_repro

  2. flutter build web --release

  3. Serve build/web (e.g. python3 -m http.server 8000 -d build/web)

  4. Load it in a browser reporting the bad tag. Either:

    (a) Naturally — run Chromium on Linux with no LANG set:

    docker run --rm -it --network host mcr.microsoft.com/playwright:v1.60.0-jammy \
      node -e "require('playwright').chromium.launch({args:['--no-sandbox']}).then(async b => {
        const p = await (await b.newContext()).newPage();
        p.on('pageerror', e => console.log('PAGEERROR:', String(e)));
        await p.goto('http://localhost:8000');
        console.log('navigator.language =', await p.evaluate(() => navigator.language));
        console.log('glass pane:', await p.locator('flt-glass-pane, flutter-view').first()
          .waitFor({state:'attached', timeout:20000}).then(() => 'ATTACHED').catch(() => 'MISSING'));
        await b.close();
      })"

    (b) Deterministically, independent of host locale — inject the tag before page scripts run:

    await page.addInitScript(() => {
      const bad = ['en-US@posix'];
      Object.defineProperty(navigator, 'languages', { configurable: true, get: () => bad });
      Object.defineProperty(navigator, 'language',  { configurable: true, get: () => bad[0] });
    });
    await page.goto('http://localhost:8000');

    (Note: setting Playwright's locale: 'en-US@posix' option is not a valid reproduction — an invalid --lang also breaks the dynamic import() of CanvasKit from gstatic.com, so the app fails to boot for an unrelated reason. Use injection.)

Expected results

An unrecognised or malformed locale tag degrades to a sensible default. The app starts. Ideally invalid entries are skipped and the remaining valid ones are honoured.

Actual results

The engine never mounts. No flt-glass-pane / flutter-view element is ever attached, so the page stays blank forever with a single uncaught error:

Error: Invalid argument(s): Incorrect locale information provided

Dart stack trace:
RangeError: Incorrect locale information provided

Because this happens during engine bootstrap, before main() runs, no application-level error handling, runZonedGuarded, or custom loader can intercept or recover from it. The user simply gets a white screen.

Root cause

EnginePlatformDispatcher.parseBrowserLanguages() in lib/web_ui/lib/src/engine/platform_dispatcher.dart constructs a DomLocale (i.e. new Intl.Locale(...)) for every entry of navigator.languages, with no try/catch and no validation:

for (final String language in languages) {
  final domLocale = DomLocale(language);
  locales.add(
    ui.Locale.fromSubtags(
      languageCode: domLocale.language,
      scriptCode: domLocale.script,
      countryCode: domLocale.region,
    ),
  );
}

and it is called from a field initializer:

PlatformConfiguration configuration = PlatformConfiguration(
  locales: parseBrowserLanguages(),
  ...
);

so one rejected tag throws while the dispatcher is being constructed. Verified unchanged on master at time of filing.

Isolated the throwing call by wrapping every locale-sensitive JS API before the bundle loads; it is specifically new Intl.Locale("en-US@posix"). Worth noting the message differs from other Intl entry points — the constructors reject the same tag with Invalid language tag: en-US@posix, while Intl.Locale reports Incorrect locale information provided, which makes the error hard to search for.

Suggested fix

Skip tags the runtime rejects rather than letting them escape, falling back to _defaultLocale only if none survive:

final locales = <ui.Locale>[];
for (final String language in languages) {
  try {
    final domLocale = DomLocale(language);
    locales.add(ui.Locale.fromSubtags(
      languageCode: domLocale.language,
      scriptCode: domLocale.script,
      countryCode: domLocale.region,
    ));
  } catch (_) {
    // A tag Intl.Locale rejects (e.g. `en-US@posix`, which Chromium reports on
    // Linux with LANG unset) must not prevent the engine from starting.
  }
}
if (locales.isEmpty) {
  return const <ui.Locale>[_defaultLocale];
}

This preserves the existing "never return an empty list" contract the function already documents.

Workaround for anyone hitting this meanwhile: sanitize navigator.languages / navigator.language in web/index.html before flutter_bootstrap.js loads, validating each tag with new Intl.Locale(tag) in a try/catch and only overriding when something is actually invalid.

Flutter Doctor output

Flutter 3.41.7 • channel stable • https://github.com/flutter/flutter.git
Framework • revision cc0734ac71 (4 months ago) • 2026-04-15 21:21:08 -0700
Engine • hash 7a53c052bc4b472cf780b199087e1368e4a9aa8c (revision 59aa584fdf) • 2026-04-16
Tools • Dart 3.11.5 • DevTools 2.54.2

Reproduced with a --release web build (CanvasKit) in Chromium 1.60.0-jammy. The offending code is identical on master today.

Activity

  1. added
    c: crashStack traces logged to the console
    a: internationalizationSupporting other languages or locales. (aka i18n)
    platform-webWeb applications specifically
    has reproducible stepsThe issue has been confirmed reproducible and is ready to work on
    team-webOwned by Web platform team
    on Aug 4, 2026
  2. mbcorona commented on Aug 4, 2026

    @mbcorona
    Member

    Confirmed from source (still present on 3.44.8): parseBrowserLanguages constructs DomLocale(language) per tag with no try/catch, from a field initializer, so a tag Intl.Locale rejects (e.g. en-US@posix) throws during engine bootstrap and the page stays blank. Clear repro and a sound suggested fix. Routing to the web team.

  3. self-assigned this
    on Aug 5, 2026
  4. added
    P1High-priority issues at the top of the work list
    triaged-webTriaged by Web platform team
    on Aug 5, 2026
  5. added a commit that references this issue on Aug 7, 2026
    5d29668
  6. added a commit that references this issue on Sep 18, 2026
    fa78ae3
  7. github-actions commented on Oct 2, 2026

    @github-actions

    This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.

  8. locked as resolved and limited conversation to collaborators on Oct 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

P1High-priority issues at the top of the work lista: internationalizationSupporting other languages or locales. (aka i18n)c: crashStack traces logged to the consolehas reproducible stepsThe issue has been confirmed reproducible and is ready to work onplatform-webWeb applications specificallyteam-webOwned by Web platform teamtriaged-webTriaged by Web platform team

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions