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 AbortFlowException
Rob
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.