I recently started seeing a crash in Crashlytics f...
# coroutines
e
I recently started seeing a crash in Crashlytics for my Android app regarding a Job getting cancelled, but it has no stacktrace. It happens when backgrounding the app, but only very rarely, so it's very difficult to track down what's actually happening. Here's the exception (it's just one line):
Copy code
Fatal Exception: JobCancellationException: Job was cancelled
Is there any sort of global debugging I can turn on to try and track this down? If so, are any of them safe for a production environment?
e
This is weird. It should have had a strack-trace.
l
I and @jw also had this happen to us back in the past in Android apps. Makes finding the root cause no longer a beginner affair.
e
If there anything that could be tweaked to get a strack-trace? What could even cause the absence of a track-trace? VM problem? Out of memory?
e
I don't think it's OOM. At least not in my scenario
l
Can it be the fact that the Android artifact of kotlinx.coroutines directly calls into
Thread.UncaughtExceptionHandler
instead of letting the exceptions/throwables be actually uncaught?
e
It should not cause the missing stack-trace, because normally UEH logs exception with stacktrace.
e
@elizarov @louiscad I got it to happen on a debug build so I got an unobfuscated version. Doesn't have much more info, but is this something that can point towards the problem:
kotlinx.coroutines.JobCancellationException: Job was cancelled; job=JobImpl{Cancelling}@57b9ffd
e
Did you get the full stacktrace?
l
I'd not be surprised if that was related to
offer
throwing before called after close, but before callback unregistering had a chance to happen. Last time I had the issue, this was the culprit. It's even nastier when that happens in non suspending function that is called from suspending function as the program/app doesn't crash, but some code is not executed, and you don't know why, until you hopefully find some code was calling
offer
on a cancelled/closed channel (e.g. because the scope of an
actor
was closed, or whetever). As a reminder, this related issue has been reported almost a year ago: https://github.com/Kotlin/kotlinx.coroutines/issues/974#issuecomment-577732745 This would be less of an issue if there was an inspection, or a checked exception that the compiler would enforce, but that's not the case, and that makes code unsafe until people realize the issue. Also, the example in the documentation of
callbackFlow
promotes an unsafe example as when there's asynchrony in callback unregistering, or multiple threads involved (like so often on Android and in the JVM), the
offer
call would crash in the cases there was a callback happening at that time. So yes, I'm trying to urge you to treat that as a design issue rather than just an "enhancement", and to fix that before Kotlin 1.4, by whatever mean (warning at use site is very conservative, but better than nothing).
e
@elizarov nope that's all there is @louiscad thanks for the info. I'll see if this leads me anywhere
Reading through that issue, if this is what's causing the crash, then it will be a nightmare to fix. This is a huge project, that was built with kotlin and coroutines from the ground up. There's heavy usage of
offer
(for better or for worse) and to handle each case would be a giant undertaking. Granted, I never read the docs closely enough to notice the caveat about throwing if it's closed, but I assumed it was a safe call because it returns Boolean. Going to post this (slightly modified) in the issue as well.
l
@eygraber You can use "Replace in Path" to replace with something like the extension I show in the initial post of the issue. Then, all you'll need is to fix the imports. If you don't use star imports, that just means using "Replace in Path" for the import as well, and then, ensure all the modules of your project build.
e
Cooler heads prevail I suppose 😁
Just an update, this looks like it completely solved my problem 😄
l
I'm very glad!