镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

fix(remote_config): deliver onConfigUpdated errors on Android and iOS as FirebaseException with stable codes - #18840

Draft
Lyokone wants to merge 3 commits into
mainfrom
fix/remote-config-android-error-thread
Draft

Lyokone wants to merge 3 commits into
mainfrom
fix/remote-config-android-error-thread

Conversation

@Lyokone

@Lyokone Lyokone commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Description

Realtime errors never reached onConfigUpdated listeners on Android or iOS. They now do, on every platform, as a typed FirebaseException.

Behaviour change: onConfigUpdated now reports realtime connection errors on Android and iOS; listen with onError. An error with no handler is reported as uncaught, which Crashlytics records as a fatal crash. The docs snippet and the example now pass onError.

Android. The ConfigUpdateListener called events.error(...) directly from onError. The SDK calls onError from its own executor: ConfigRealtimeHttpClient.propagateErrors runs in continueWith(scheduledExecutorService) (and ConfigAutoFetch reports from the same executor). EventSink.error sends a platform message, and FlutterJNI.dispatchPlatformMessage throws when it is not on the main thread. So Dart never saw the error, and the RuntimeException was thrown into the SDK's task continuation, where it disappeared. onUpdate already posts to the main thread; onError now does the same.

iOS. The listener only printed the error. It now forwards it to the stream on the main queue.

Typed errors. Before this change, the error reached Dart as PlatformException('firebase_remote_config', message, details: null) on Android and iOS, so the native code was lost, and as the raw JS FirebaseError on web. Every platform now delivers FirebaseException(plugin: 'firebase_remote_config', code: ..., message: ...):

Code Android FirebaseRemoteConfigException.Code iOS RemoteConfigUpdateError Web (JS SDK)
config-update-stream-error CONFIG_UPDATE_STREAM_ERROR streamError remoteconfig/stream-error
config-update-message-invalid CONFIG_UPDATE_MESSAGE_INVALID messageInvalid remoteconfig/update-message-invalid
config-update-not-fetched CONFIG_UPDATE_NOT_FETCHED notFetched remoteconfig/update-not-fetched
config-update-unavailable CONFIG_UPDATE_UNAVAILABLE unavailable remoteconfig/realtime-unavailable
unknown UNKNOWN other —
  • Android and iOS put the code and message in the error details, as they already do for fetch errors. The method-channel stream converts them with receiveGuardedBroadcastStream and the package's existing convertPlatformException.
  • The message keeps the HTTP status when the SDK reports one, e.g. HTTP status code: 503.
  • On iOS the error domain is checked first. RemoteConfigUpdateError codes 8001–8004 overlap with RemoteConfigError codes 8001–8003, so notFetched (8002) would otherwise have been reported as throttled. A failed fetch of an announced update arrives in RemoteConfigErrorDomain, and keeps the codes fetch uses.
  • On web, onConfigUpdated now goes through the package's convertWebExceptions. That function now maps the JS SDK's realtime codes to the names above. Its existing prefix strip expects remote_config/ but the JS SDK uses remoteconfig/; the fix applies only to the realtime codes.
  • The stream stays open after an error. The onConfigUpdated dartdoc lists the codes and says to pass onError.

Reproduced with the remote_config example and a local, uncommitted hook that calls the SDK's own error path (ConfigRealtimeHttpClient.propagateErrors on its scheduledExecutorService on Android, RCNConfigRealtime propagateErrors: on iOS), while a Dart test listens to onConfigUpdated with onError:

main first commit of this PR
Android emulator propagateErrors throws RuntimeException: Methods marked with @UiThread must be executed on the main thread. Current thread: Firebase Blocking Thread #1, and Dart receives nothing within 30 s Dart receives the error
iOS 27 simulator nothing within 30 s (printed only) Dart receives the error

The hook was needed because the realtime errors seen in practice are hard to produce on demand: with an invalid API key both SDKs keep retrying with backoff for minutes before they report one.

Notes for reviewers

Tests

  • Dart (firebase_remote_config_platform_interface, method_channel_firebase_remote_config_test.dart): a mocked native stream sends each code, then an update. The tests check that the stream emits FirebaseExceptions with those codes and messages, that the update after an error is still delivered, and that an error without details is reported as unknown. All 3 fail on the previous commit (the stream emitted PlatformException) and pass here.
  • Web (firebase_remote_config_web/test/internals_test.dart, Chrome): JS FirebaseErrors with the four realtime codes come out of convertWebExceptions with the stable codes, and the stream stays open. It fails before the fix (the code was remoteconfig/stream-error) and passes here.
  • JUnit (FirebaseRemoteConfigPluginTest, run by android-unit-tests as :firebase_remote_config:testDebugUnitTest). The tests use an EventSink that throws off the main thread, as FlutterJNI does:
    • onError_isForwardedToDartOnTheMainThread: fails on main with the FlutterJNI exception above.
    • onError_reportsAStableCodeForEachNativeCode covers every Code value, and onError_keepsTheHttpStatusInTheMessage covers the HTTP status. Both fail when details is null.
    • onListen_registersAListenerThatReportsOnTheMainThread drives onListen and onCancel through an injected registration function. It fails if onListen builds its own listener or bypasses the main-thread executor.
    • onUpdate_isForwardedToDartOnTheMainThread guards the existing behaviour.
  • iOS: the example builds for the simulator with Firebase iOS SDK 13.0.0. The forwarding was verified by the simulator run above. An e2e test would need a way to make the backend fail the realtime stream, which CI doesn't have.

Related Issues

May explain part of #17898: after the realtime connection gives up, apps were never told, so they could not listen again.

Checklist

  • I read the Contributor Guide and followed the process outlined there for submitting PRs.
  • My PR includes unit or integration tests for all changed/updated/fixed behaviors (See Contributor Guide).
  • All existing and new tests are passing.
  • I updated/added relevant documentation (doc comments with ///).
  • The analyzer (melos run analyze) does not report any problems on my PR.
  • I read and followed the Flutter Style Guide.
  • I signed the CLA.
  • I am willing to follow-up on review comments in a timely manner.

Breaking Change

  • No, this is not a breaking change.

Apps that listen without onError will now see realtime errors as uncaught errors on Android and iOS, as web already did. On web, these errors are now FirebaseExceptions instead of raw JS objects.

… and iOS

On Android the realtime listener called EventSink.error from the SDK's
background executor. FlutterJNI throws off the main thread, so the error
never reached Dart and the exception was swallowed by the SDK's task
continuation. Post it to the main thread, as onUpdate already does.

On iOS the error was only printed. Forward it to the stream as a
FlutterError with the same code as Android, matching web, which already
adds realtime errors to the stream.
@gemini-code-assist

Copy link
Copy Markdown
Contributor
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

…n with stable codes

onConfigUpdated now reports realtime connection errors on Android and
iOS; listen with onError. An error with no handler is reported as
uncaught, which Crashlytics records as a fatal crash. The stream stays
open after an error, and the SDKs keep retrying, so the same error can
be reported again.

Every platform now delivers these errors as
FirebaseException(plugin: 'firebase_remote_config') with one of these
codes: config-update-stream-error, config-update-message-invalid,
config-update-not-fetched, config-update-unavailable or unknown.
Before, Android and iOS sent a PlatformException with no details, which
lost the native code, and web sent the raw JS FirebaseError.

- Android and iOS put the code and message in the error details, and
  the method channel stream converts them with the package's existing
  convertPlatformException.
- On iOS, the update error domain is checked first. Its codes overlap
  with RemoteConfigError (notFetched and throttled are both 8002). A
  failed fetch of an announced update keeps the codes fetch uses.
- On web, onConfigUpdated goes through convertWebExceptions, which now
  maps the JS SDK's realtime codes to the same names.

The onConfigUpdated dartdoc lists the codes. The docs snippet and the
example now pass onError.
@Lyokone Lyokone changed the title fix(remote_config): deliver onConfigUpdated errors to Dart on Android and iOS fix(remote_config): deliver onConfigUpdated errors on Android and iOS as FirebaseException with stable codes Oct 21, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant