Rob
07/27/2025, 4:35 PMursus
07/27/2025, 5:34 PMRob
07/27/2025, 5:36 PMCancellationException is also thrown in cases where the coroutine is still active.
https://github.com/Kotlin/kotlinx.coroutines/issues/3658ursus
07/27/2025, 5:53 PMursus
07/27/2025, 5:54 PMRob
07/27/2025, 5:57 PMIMO, the great takeaway from the original post on the "The Silent Killer" is this pattern:
try {
doStuff()
} catch (error: Throwable) {
coroutineContext.ensureActive()
handleError(error)
}
That should be the recommended pattern to use instead of trying to catch CancellationException and ignoring it. That is a clear documentation thing, but we might also consider doing some utilities to help with it.ursus
07/27/2025, 5:58 PMRob
07/27/2025, 6:02 PMCoroutineCancellation like a normal exception and try to recover. Please read this as well to fully understand why rethrowing is not always the correct.
https://medium.com/better-programming/the-silent-killer-thats-crashing-your-coroutines-9171d1e8f79blouiscad
07/29/2025, 7:55 PMensureActive() is technically problematic if you wrap calls to flow's emit(…) with a try catch as operators like first() throw a CancellationException without a middle cancelled scope to tell you it's no longer active.ursus
07/29/2025, 7:57 PMlouiscad
07/29/2025, 7:58 PMCancellationException and I don't use them for recoverable cancellation.ursus
07/29/2025, 7:59 PMRob
07/29/2025, 8:01 PMlouiscad
07/29/2025, 8:01 PMemit is more than weird too. To me, both are totally fine, I avoid weird things that play with what coroutines rely on.louiscad
07/29/2025, 8:04 PMval stuff = flow {
while (true) try {
emit(whatever())
delay(whoCaresHowLong)
} catch (e: Exception) {
e.ensureActive()
reportExceptionSomehow(e)
}
}.first()
This looks fine, but the loop won't be interrupted on the first value, and an infinite loop will ensue.louiscad
07/29/2025, 8:05 PMCancellationException works. This is because of the optimized implementation of first().ursus
07/29/2025, 8:05 PMlouiscad
07/29/2025, 8:06 PMCancellationException for recoverable cancellation (and I firmly believe it's not a good idea, even though I have been doing it a few times in the past)Rob
07/29/2025, 8:07 PMe.ensureActive() should be currentCoroutineContext().ensureActive(). Exceptions don't implement ensureActive(). I don't see anything problematic.Rob
07/29/2025, 8:23 PMRob
07/29/2025, 8:35 PMflow doesn't have it's own Job. That is a subtle bug but really useful to know. I'll see if I can make it check for AbortFlowExceptionRob
07/29/2025, 8:54 PMRob
07/29/2025, 9:29 PMFlowCollector , it should be detektable.Rob
07/30/2025, 1:26 AMchannelFlow does create a new Job which is does get cancelled after getting the first element.