kotlinx.coroutines: IllegalStateException : Already resumed
Hi,
I am observing live data using observeForever() and then removing the observer later. I handle success and failure scenarios and resume the continuation object accordingly.
//failure
removeObserver(observer)
if (coroutine.isActive) coroutine.resume(getErrorState())
//success
removeObserver(observer)
if (coroutine.isActive) coroutine.resume(data)
In most cases it works fine. However, randomly few times I get exception :
Fatal Exception: java.lang.IllegalStateException: Already resumed, but proposed with update android.widget.RemoteViews@ebbd63d at kotlinx.coroutines.CancellableContinuationImpl.alreadyResumedError(CancellableContinuationImpl.java:277) at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl(CancellableContinuationImpl.java:272) at kotlinx.coroutines.CancellableContinuationImpl.resumeWith(CancellableContinuationImpl.java:189)
It seems , the isActive check isn’t behaving as expected. Please suggest a better check to avoid this crash.
Cheers !
About this issue
- Original URL
- State: closed
- Created 4 years ago
- Reactions: 1
- Comments: 18 (6 by maintainers)
Here are my 2 cents, I had the same issue when I wrapped an API callback into a suspendCancellableCoroutine, the callback inside was receiving calls in different methods, therefore cont.resume was being called twice, what I ended up doing was an extension function to only call resume if the cont was active, like:
for my case, this works since I only care about the first call to cont.resume
@aruke a strategy I used to use was to save the continuation in a nullable var, resuming from the var and immediately setting it to null afterwards. Then, if other callbacks eventually try to resume it again, field will be null.
But honestly, it works but it’s flaky design. In my opinion you should only suspend and resume if there’s a single callback that is guaranteed to always run and run exactly once. For example, GMS tasks and
addOnCompleteListener.If you have a code snippet to share I could give further input.
Calling
resume, usually, just schedules coroutine for execution in the corresponding coroutine dispatcher.It looks like you call
resumetwice on the same continuation.@elizarov Can you please look into this issue again? We have a simpler use-case where the issue is reproduced, hopefully it will be useful for you:
The exception stacktrace:
@elizarov I’m having a similar issue in my app. My question is why coroutine doesn’t resume immediately after
Continuation.resumegets called? That way we’d never get this sort of exception (callingresumeon an already resumedContinuation. I’m certain there is some reasons behind. Interested to hear your guidance.It just means that you called
resumeon the same continuation twice. It is hard to figure out how this might happen just by a snippet of your code.