This is a painfully familiar problem to anyone working with async APIs: when your call encounters a failure, the async library's private thread detects it, creates an exception object, and passes it to your callback. The only info worth a dime in that exception is its message and possibly its type. The stack trace is worthless.
Cross-breed that with Google Play's way of reporting the app crashes: the message is stripped and all you get is the stack trace. Now you've got nothing to go with. You just know your app has a bug that you didn't detect in your own testing.
Kotlin's coroutines at least give us some hope that this could be made better. The coroutine stack trace is conceptually there, just difficult to extract. However, currently the stack traces we get are the same, useless ones I described above.
I'm familiar with the kotlinx-coroutines-debug module and I see there are some provisions on the implementation side to recreate the coroutine stack trace, but how can I make use of these facilities in a production app installed on my user's smartphone?
I faced the same problem. The Kotlin coroutine debug library didn't help me in any way. Therefore, after studying the implementation of coroutines, I wrote my own solution based on bytecode generation and MethodHandle API. It supports JVM 1.8 and Android API 26 or higher. I called it Stacktrace-decoroutinator.
The reason why the stacktrace is lost is that when the coroutine wakes up, only the last method of its call stack is called.
My library replaces the coroutine awakening implementation. It generates classes at runtime with names that match the entire coroutine call stack. These classes don't do anything except call each other in the coroutine call stack sequence.
Thus, if the coroutine throws an exception, they mimic the real call stack of the coroutine during the creation of the exception stacktrace.
Kotlin plugin for IntelliJ IDEA provides this possibility since version 1.4 (for versions 1.3.8 or later of kotlinx-coroutines-core).