Steps to Reproduce
- 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.
- Complete the authorization successfully so
Delegate.onActivityResult resolves the pending callback once.
- 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!
Steps to Reproduce
GoogleSignIn.instance.authorizationClient.authorizeServer(scopes)(orauthorizeScopes) — anything that routes throughREQUEST_CODE_AUTHORIZEand shows the consent/resolution UI.Delegate.onActivityResultresolves the pending callback once.REQUEST_CODE_AUTHORIZEactivity result delivered toonActivityResulta 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
RuntimeExceptionwhose cause is:Outer frame:
Root cause
In
GoogleSignInPlugin.java(Delegate), the authorization callback is a one-shot field set inauthorize(...):But
onActivityResultclears it only on the failure path — the success pathreturns before reachingpendingAuthorizationCallback = null;:Because the success path leaves
pendingAuthorizationCallbacknon-null, a second delivery of theREQUEST_CODE_AUTHORIZEresult re-enters theif (pendingAuthorizationCallback != null)branch and callscompleteWithValueon an already-completed reply, throwingIllegalStateException: Reply already submitted. (In this report the second delivery hit thecatchbranch at line 499 becausegetAuthorizationResultFromIntentthrew 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:
Notes
activity-alias, so this is distinct from the (bot-closed) [google_sign_in] Crash on Android when using google_sign_in with activity-alias for dynamic app icon switching #169344, though it shares the same "reply completed twice" failure mode. Likely related:GoogleSignIn().signIn()hangs in every call; after the first call no popup is shown. #165910, [google_sign_in] Switch Android to Credential Manager #154205.authorizationClientAPI, not the legacysignIn()flow.google_sign_in_androidthrough the latest published version (7.2.13); no changelog entry addresses it.Versions
google_sign_in: 7.2.0google_sign_in_android: 7.2.10(also present in 7.2.13)flutter doctor -v