Skip to content

[google_sign_in_android] IllegalStateException: Reply already submitted — Delegate.onActivityResult does not clear pendingAuthorizationCallback on success path #188062

Description

@golanshy

Steps to Reproduce

  1. Use the v7 authorization flow on Android, e.g. GoogleSignIn.instance.authorizationClient.authorizeServer(scopes) (or authorizeScopes) — anything that routes through REQUEST_CODE_AUTHORIZE and shows the consent/resolution UI.
  2. Complete the authorization successfully so Delegate.onActivityResult resolves the pending callback once.
  3. Have the REQUEST_CODE_AUTHORIZE activity result delivered to onActivityResult a second time for the same request (e.g. activity recreation / re-delivered result after a configuration change or process state change).

Expected results

A duplicate/late delivery of the authorization activity result should be a no-op (at most logged as "Unexpected authorization result callback"). It must not complete the Flutter reply twice.

Actual results

The app crashes with an uncaught RuntimeException whose cause is:

Caused by java.lang.IllegalStateException: Reply already submitted
  at io.flutter.embedding.engine.dart.DartMessenger$Reply.reply (DartMessenger.java:425)
  at io.flutter.plugin.common.BasicMessageChannel$IncomingMessageHandler$1.reply (BasicMessageChannel.java:266)
  at io.flutter.plugins.googlesignin.GoogleSignInApi$Companion.setUp$lambda$14$lambda$13$lambda$12 (GoogleSignInApi.java:793)
  at io.flutter.plugins.googlesignin.ResultUtilsKt.completeWithValue (ResultUtils.kt:16)
  at io.flutter.plugins.googlesignin.GoogleSignInPlugin$Delegate.onActivityResult (GoogleSignInPlugin.java:499)
  at io.flutter.embedding.engine.FlutterEngineConnectionRegistry$FlutterEngineActivityPluginBinding.onActivityResult (FlutterEngineConnectionRegistry.java:795)
  at io.flutter.embedding.engine.FlutterEngineConnectionRegistry.onActivityResult (FlutterEngineConnectionRegistry.java:443)
  at io.flutter.embedding.android.FlutterActivityAndFragmentDelegate.onActivityResult (FlutterActivityAndFragmentDelegate.java:1006)
  at io.flutter.embedding.android.FlutterActivity.onActivityResult (FlutterActivity.java:926)
  at android.app.Activity.onActivityResult (Activity.java:7872)
  at android.app.Activity.internalDispatchActivityResult (Activity.java:9961)
  at android.app.Activity.dispatchActivityResult (Activity.java:9938)
  at android.app.ActivityThread.deliverResults (ActivityThread.java:6907)

Outer frame:

Exception java.lang.RuntimeException:
  at android.app.ActivityThread.deliverResults (ActivityThread.java:6918)
  at android.app.ActivityThread.handleSendResult (ActivityThread.java:6957)
  ...
  at android.app.ActivityThread.main (ActivityThread.java:10060)

Root cause

In GoogleSignInPlugin.java (Delegate), the authorization callback is a one-shot field set in authorize(...):

// authorize(), ~line 414
pendingAuthorizationCallback = callback;
activity.startIntentSenderForResult(pendingIntent.getIntentSender(), REQUEST_CODE_AUTHORIZE, ...);

But onActivityResult clears it only on the failure path — the success path returns before reaching pendingAuthorizationCallback = null;:

@Override
public boolean onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
  if (requestCode == REQUEST_CODE_AUTHORIZE) {
    if (pendingAuthorizationCallback != null) {
      try {
        AuthorizationResult authorizationResult =
            authorizationClientFactory.create(context).getAuthorizationResultFromIntent(data);
        ResultUtilsKt.completeWithValue(pendingAuthorizationCallback, /* ... */);
        return true;                       // <-- early return: callback NOT nulled
      } catch (ApiException e) {
        ResultUtilsKt.completeWithValue(   // <-- line 499 (the crash site)
            pendingAuthorizationCallback,
            new AuthorizeFailure(AuthorizeFailureType.API_EXCEPTION, e.getMessage(), null));
      }
      pendingAuthorizationCallback = null; // <-- only reached via the catch path
    } else {
      Log.e("google_sign_in", "Unexpected authorization result callback");
    }
  }
  return false;
}

Because the success path leaves pendingAuthorizationCallback non-null, a second delivery of the REQUEST_CODE_AUTHORIZE result re-enters the if (pendingAuthorizationCallback != null) branch and calls completeWithValue on an already-completed reply, throwing IllegalStateException: Reply already submitted. (In this report the second delivery hit the catch branch at line 499 because getAuthorizationResultFromIntent threw on the stale intent, but the success branch at line 491 is equally affected.)

Suggested fix

Capture and null the callback before completing it, so any re-delivery is a harmless no-op:

if (pendingAuthorizationCallback != null) {
  Function1<? super Result<? extends AuthorizeResult>, Unit> callback = pendingAuthorizationCallback;
  pendingAuthorizationCallback = null;     // clear first
  try {
    AuthorizationResult authorizationResult =
        authorizationClientFactory.create(context).getAuthorizationResultFromIntent(data);
    ResultUtilsKt.completeWithValue(callback, /* ... */);
  } catch (ApiException e) {
    ResultUtilsKt.completeWithValue(callback,
        new AuthorizeFailure(AuthorizeFailureType.API_EXCEPTION, e.getMessage(), null));
  }
  return true;
}

Notes

Versions

  • google_sign_in: 7.2.0
  • google_sign_in_android: 7.2.10 (also present in 7.2.13)
  • Flutter 3.44.1 (stable), Dart 3.12.1
  • Affected devices: Android (multiple OS versions; crash observed in production)
flutter doctor -v
[✓] Flutter (Channel stable, 3.44.1, on macOS 26.5.1 25F80 darwin-arm64, locale en-GB)
    • Flutter version 3.44.1 on channel stable at /opt/homebrew/share/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 924134a44c (3 weeks ago), 2026-05-29 12:13:22 -0400
    • Engine revision c416acfeb8
    • Dart version 3.12.1
    • DevTools version 2.57.0

[✓] Android toolchain - develop for Android devices (Android SDK version 37.0.0)
    • Platform android-37.0, build-tools 37.0.0
    • Java version OpenJDK Runtime Environment (build 21.0.10+-117844308-b1163.108)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 26.5)
[✓] Chrome - develop for the web
[✓] Network resources

• No issues found!

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1High-priority issues at the top of the work listp: google_sign_inThe Google Sign-In pluginpackageflutter/packages repository. See also p: labels.platform-androidAndroid applications specificallyteam-androidOwned by Android platform teamtriaged-androidTriaged by Android platform team

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions