Skip to content

[firebase_core_web]: FirebaseCoreWeb.app() masks the real error with an unsafe as JSError cast (same pattern as #18176, not covered by that fix) #18548

Description

@zornsully

Is there an existing issue for this?

  • I have searched the existing issues.

Which plugins are affected?

Core

Which platforms are affected?

Web

Description

FirebaseCoreWeb.app() catches any exception thrown while resolving the JS SDK's app instance and unconditionally casts it to JSError, with no is JSError guard first:

https://github.com/firebase/flutterfire/blob/main/packages/firebase_core/firebase_core_web/lib/src/firebase_core_web.dart#L423-L436

FirebaseAppPlatform app([String name = defaultFirebaseAppName]) {
  firebase.App app;

  try {
    app = guardNotInitialized(() => firebase.app(name));
    return _createFromJsApp(app);
  } catch (e) {
    if (_getJSErrorCode(e as JSError) == 'app/no-app') {   // <-- explicit unchecked cast
      throw noAppExists(name);
    }

    throw _catchJSError(e);   // <-- e is passed positionally into `_catchJSError(JSError e)` — implicit cast
  }
}

The same unguarded pattern also exists a few lines earlier, in the secondary-app branch (line 389).

guardNotInitialized, in the same file, can itself throw a plain Dart FirebaseException — not a JSError — whenever the underlying firebase.app(name) JS call fails with an error whose message contains "of undefined":

Never _handleException(Object exception, StackTrace stackTrace) {
  if (exception.toString().contains('of undefined')) {
    throw coreNotInitialized();   // FirebaseException, not JSError
  }
  Error.throwWithStackTrace(exception, stackTrace);
}

So when that specific condition is hit, app()'s own catch block receives a FirebaseException (from coreNotInitialized()), immediately force-casts it to JSError, and throws a brand-new, unrelated TypeError that completely masks the original, actionable "Firebase core not initialized" exception.

This is the same anti-pattern already reported and fixed in _flutterfire_internals's _testException via #18176 / PR #18177 ("propagates plain Dart errors from Futures (e.g. ArgumentError on web)") — but that fix didn't cover this instance in firebase_core_web.dart's own app() method, which still has the identical unguarded-cast shape as of both the latest published version (3.10.0) and the current main branch (checked directly, same line numbers: 389, 393, 430, 434).

Reproducing the issue

Reliably reproduced in production (Chrome, Flutter Web) on a real app: an authenticated action that resolves FirebaseAuth.instance (login, Google sign-in) hits this path and crashes every time with the masking TypeError instead of a usable error message. An isolated minimal repro outside our app wasn't built, but the mechanism above is confirmed by decoding our production main.dart.js build's minified stack trace against its own source map back to firebase_core_web.dart:434 (the throw _catchJSError(e); line), on two separate builds — one on firebase_core_web 3.9.1, one on 3.10.0 after upgrading specifically to test whether #18436's fix (the concurrent script-loading / WebKit temporal-dead-zone race) resolved it. It did not — the crash signature and decoded throw site are identical on both versions, consistent with this being a separate, still-present bug rather than the race #18436 already fixed.

Suspected trigger: some transient condition causes the underlying firebase.app(name) JS call to fail with an error whose message contains "of undefined" (per _handleException's own check), converting it to coreNotInitialized(). In our app this happens on a later call to resolve the app/auth instance, after an earlier, successful touch of FirebaseAuth.instance during startup already completed without error — so whatever the underlying race is, it isn't limited to the very first access.

Firebase Core version

4.13.0

Flutter Version

3.44.8 (stable channel, Dart 3.12.2)

Relevant Log Output

Uncaught Error: TypeError: Instance of 'minified:Ps': type 'minified:Ps' is not a subtype of type 'minified:eg'

    at oi.bX0 [as a] (main.dart.js:4909:23)
    at oi.bXj (main.dart.js:4881:10)
    at aDT.hP (main.dart.js:76436:14)
    at bne.$1 (main.dart.js:60116:44)
    at pW.oT (main.dart.js:142805:22)
    at KK.oT (main.dart.js:142809:28)
    at KK.aef (main.dart.js:141769:9)
    at KK.b1D (main.dart.js:141735:7)
    at KK.vR (main.dart.js:141758:3)
    at pW.Hu (main.dart.js:142194:3)
    ... (rethrown 3x further up through the app's own Riverpod provider-construction call chain)

Decoded against our own build's source map:

main.dart.js:4909:23  -> dart-sdk rti.dart:1503  (_generalAsCheckImplementation)      <- the failing cast itself
main.dart.js:4881:10  -> dart-sdk rti.dart:1319  (_installSpecializedAsCheck)
main.dart.js:76436:14 -> firebase_core_web-3.10.0/lib/src/firebase_core_web.dart:434  (FirebaseCoreWeb.app)
main.dart.js:60116:44 -> firebase_core_platform_interface-8.1.0/.../platform_interface_firebase_app.dart:19 (FirebaseApp._)

(remaining frames are our own app's Riverpod provider-construction call chain, not FlutterFire-specific)

Flutter dependencies

firebase_core: 4.13.0
firebase_core_web: 3.10.0
firebase_core_platform_interface: 8.1.0
firebase_auth: 6.5.7
firebase_auth_web: 6.2.6
firebase_auth_platform_interface: 9.0.6
cloud_firestore: 6.8.0
cloud_firestore_web: 5.7.2
_flutterfire_internals: 1.3.76

Additional context and comments

Suggested fix, mirroring PR #18177's own resolution of the identical pattern in _flutterfire_internals:

} catch (e) {
  if (e is! JSError) {
    Error.throwWithStackTrace(e, StackTrace.current); // preserve the real error (e.g. coreNotInitialized())
  }
  if (_getJSErrorCode(e) == 'app/no-app') {
    throw noAppExists(name);
  }
  throw _catchJSError(e);
}

(and the analogous change at the secondary-app branch around line 389).

Happy to open a PR with this change if a maintainer confirms the approach — filing the issue first since there's no fully isolated minimal repro for the underlying "of undefined" trigger, only the confirmed masking-bug mechanism downstream of it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Needs AttentionThis issue needs maintainer attention.platform: webIssues / PRs which are specifically for web.plugin: coretype: bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions