amplify-android: CredentialStoreClient$loadCredentials$2$1.invoke java.lang.IllegalStateException - Already resumed

Before opening, please confirm:

Language and Async Model

Kotlin

Amplify Categories

Authentication

Gradle script dependencies

com.amplifyframework:core-kotlin:2.4.0
com.amplifyframework:aws-auth-cognito:2.4.0

Environment information

Welcome to Gradle 7.5!

Here are the highlights of this release:
 - Support for Java 18
 - Support for building with Groovy 4
 - Much more responsive continuous builds
 - Improved diagnostics for dependency resolution

For more details see https://docs.gradle.org/7.5/release-notes.html


------------------------------------------------------------
Gradle 7.5
------------------------------------------------------------

Build time:   2022-07-14 12:48:15 UTC
Revision:     c7db7b958189ad2b0c1472b6fe663e6d654a5103

Kotlin:       1.6.21
Groovy:       3.0.10
Ant:          Apache Ant(TM) version 1.10.11 compiled on July 10 2021
JVM:          17 (Oracle Corporation 17+35-2724)
OS:           Mac OS X 13.2.1 x86_64


Please include any relevant guides or documentation you’re referencing

No response

Describe the bug

I am using Amplify Authentication library. I am trying to call this function multiple times in the background when it return false,

private suspend fun getAuthSessionAndSetAccessTokenWhenRefreshed(): AWSCognitoAuthSession? {
        return try {
            val authSession = Amplify.Auth.fetchAuthSession() as AWSCognitoAuthSession
            val idResult = authSession.identityIdResult
            if (idResult.type == AuthSessionResult.Type.SUCCESS) {
                authSession
            } else {
                if (idResult.error is SessionExpiredException) {
                    if (idResult.error?.cause is NotAuthorizedException) {
                        logout()
                        authSession
                    } else {
                      AuthFetchSessionOptions.builder().forceRefresh(true).build().let { option ->
                            (Amplify.Auth.fetchAuthSession(option) as AWSCognitoAuthSession).also { session ->
                                val newIdentityIdResult = session.identityIdResult
                                if (newIdentityIdResult.type == AuthSessionResult.Type.FAILURE && newIdentityIdResult.error?.cause is NotAuthorizedException) {
                                       logout()
                                } else {
                                    val tokenResult = session.userPoolTokensResult
                                    if (tokenResult.type == AuthSessionResult.Type.SUCCESS) {
                                        setAccessToken(tokenResult.value?.accessToken)
                                    }
                                }
                            }
                        }
                    }
                } else {
                    authSession
                }
            }
        } catch (exception: AuthException) {
            null
        }
    }

But sometimes I found there are some crash happening in the Amplify library. Here is the crash output,

Fatal Exception: java.lang.IllegalStateException: Already resumed
       at kotlin.coroutines.SafeContinuation.resumeWith(SafeContinuationJvm.kt:44)
       at com.amplifyframework.auth.cognito.CredentialStoreClient$loadCredentials$2$1.invoke(CredentialStoreClient.kt:97)
       at com.amplifyframework.auth.cognito.CredentialStoreClient$loadCredentials$2$1.invoke(CredentialStoreClient.kt:95)
       at com.amplifyframework.auth.cognito.CredentialStoreClient$listenForResult$1.invoke(CredentialStoreClient.kt:77)
       at com.amplifyframework.auth.cognito.CredentialStoreClient$listenForResult$1.invoke(CredentialStoreClient.kt:60)
       at com.amplifyframework.statemachine.StateMachine.notifySubscribers(StateMachine.kt:176)
       at com.amplifyframework.statemachine.StateMachine.process(StateMachine.kt:191)
       at com.amplifyframework.statemachine.StateMachine.access$process(StateMachine.kt:49)
       at com.amplifyframework.statemachine.StateMachine$send$1.invokeSuspend(StateMachine.kt:160)
       at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
       at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
       at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:463)
       at java.util.concurrent.FutureTask.run(FutureTask.java:264)
       at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:307)
       at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1137)
       at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:637)
       at java.lang.Thread.run(Thread.java:1012)

I went to check the library code to see where this crash happening. I found it is happening in below code where continuation gets called multiple time,

    override suspend fun loadCredentials(credentialType: CredentialType): AmplifyCredential {
        return suspendCoroutine { continuation ->
            listenForResult(
                CredentialStoreEvent(CredentialStoreEvent.EventType.LoadCredentialStore(credentialType)),
                { continuation.resumeWith(it) },
                { continuation.resumeWithException(it) }
            )
        }
    }

Could you tell me what to do to prevent this error happening as it crashing my app.

Reproduction steps (if applicable)

No response

Code Snippet

// Put your code below this line.

Log output

// Put your logs below this line


amplifyconfiguration.json

No response

GraphQL Schema

// Put your schema below this line


Additional information and screenshots

No response

About this issue

  • Original URL
  • State: closed
  • Created a year ago
  • Comments: 29 (12 by maintainers)

Most upvoted comments

I’ve taken some time today to diagnose and should have a PR up today, and once merged, a fix should go out in the next release.

This bug should be fixed in the latest release (2.11.2). Thanks everyone for your reports!

@reisfernanda Thank you for letting us know. We will continue looking to find the root cause.

@reisfernanda Thank you for your report, especially around the details of the crash happening within the first 2 seconds of the users session.

With the other reports happening while the user is in the background, I’m wondering if its possible these crashes are happening with the application is being recreated, running through the app’s onCreate, and calling Amplify.configure. @minaairsupport I know you have mentioned the crash is being seen 5-6 days after launch, but is it possible the app is being re-launched in the background, rather than a single continuous session?

There is a but that I worked on in Amplify v2.8.x that seems unrelated at first glance, but may resolve this issue. https://github.com/aws-amplify/amplify-android/pull/2402

Here, It is possible that fetchAuthSession could be called before the sdk has fully configured (part of the configuration happens asynchronously). This PR ensures that customer calls are delayed until Amplify Auth has fully configured.

There’s a migration process to migrate credentials from v1 -> v2 of Amplify that happens upon each Amplify.configure call. If fetchAuthSession (or a similar call is made while configuration is in process), it may be possible that in rare scenarios, both processes hit our CredentialStore state machine at the same time, causing multiple fires into the continuation.

While we will continue investigating and searching for a root cause, it may be beneficial to upgrade to the latest version of Amplify to see if the crash was resolved with the linked bug fix.

listenForResult should only ever call it’s onSuccess or onError 1 single time. As a result, the continuation should only be called 1 time.

While we can further mitigate the continuation being fired twice, it would only be masking the actual problem. We’re still looking at all paths to determine what might be causing the continuation to fire a second time.

@androidcodehunter can you enable logging in the application class by adding the following line BEFORE your auth plugin addition and please paste your logs when the exception occurs. This will help us understand the flow from where the problem is potentially occurring from and fix it since we are unable to reproduce it locally.

Amplify.addPlugin(AndroidLoggingPlugin(LogLevel.VERBOSE))