# Kotlin coroutines stack trace problem

**URL:** <https://discuss.kotlinlang.org/t/kotlin-coroutines-stack-trace-problem/23847>\
**Category:** Libraries\
**Created:** [January 5, 2022, 12:45pm UTC](https://discuss.kotlinlang.org/t/kotlin-coroutines-stack-trace-problem/23847 "2022-01-05T12:45:30Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![reformator](https://avatars.discourse-cdn.com/v4/letter/r/3ab097/32.png) [@reformator](https://discuss.kotlinlang.org/u/reformator)\
**Post date:** [January 5, 2022, 12:45pm UTC](https://discuss.kotlinlang.org/t/kotlin-coroutines-stack-trace-problem/23847/1 "2022-01-05T12:45:30Z")

</div>

Kotlin coroutines is a great feature that allows you to write asynchronous code in synchronous style.  
It’s absolutely perfect until you need to investigate problems in your code.

One of the common problems with coroutines is the shortened stack trace in exceptions thrown in coroutines. For example, this code prints out the stack trace below:

```auto
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking

suspend fun fun1() {
    delay(100)
    throw Exception("exception at ${System.currentTimeMillis()}")
}

suspend fun fun2() {
    fun1()
    delay(100)
}

suspend fun fun3() {
    fun2()
    delay(100)
}

fun main() {
    try {
        runBlocking {
            fun3()
        }
    } catch (e: Exception) {
        e.printStackTrace()
    }
}

```

```auto
java.lang.Exception: exception at 1641336576296
  at MainKt.fun1(main.kt:6)
  at MainKt$fun1$1.invokeSuspend(main.kt)
  at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
  at kotlinx.coroutines.DispatchedTaskKt.resume(DispatchedTask.kt:234)
  at kotlinx.coroutines.DispatchedTaskKt.dispatch(DispatchedTask.kt:166)
  at kotlinx.coroutines.CancellableContinuationImpl.dispatchResume(CancellableContinuationImpl.kt:397)
  at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl(CancellableContinuationImpl.kt:431)
  at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl$default(CancellableContinuationImpl.kt:420)
  at kotlinx.coroutines.CancellableContinuationImpl.resumeUndispatched(CancellableContinuationImpl.kt:518)
  at kotlinx.coroutines.EventLoopImplBase$DelayedResumeTask.run(EventLoop.common.kt:494)
  at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:279)
  at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:85)
  at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:59)
  at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
  at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:38)
  at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
  at MainKt.main(main.kt:21)
  at MainKt.main(main.kt)

```

The stack trace doesn’t represent the true coroutine call stack when the exception was created: calls of functions fun3 and fun2 are absent. On complex systems, this problem can make debugging much more difficult.

The Kotlin team are known about the problem and has come up with a [solution](https://github.com/Kotlin/kotlinx.coroutines/blob/master/docs/topics/debugging.md#stacktrace-recovery), but it solves just a part of the cases.

The reason why the stack trace is shortened is that when the coroutine wakes up, only the last method of its call stack is called.  
Therefore, after studying the implementation of coroutines, I wrote my own solution based on bytecode generation and MethodHandle API. I called it [Stacktrace-decoroutinator](https://github.com/Anamorphosee/stacktrace-decoroutinator). 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 order. Thus, if the coroutine throws an exception, they mimic the real call stack of the coroutine during the creation of the exception stacktrace.  
In the near future I am going to test the library in my production environment and I will be glad if someone else will join.

---

<div class="post-metadata">

**Author:** ![broot](https://avatars.discourse-cdn.com/v4/letter/b/a88e57/32.png) [@broot](https://discuss.kotlinlang.org/u/broot)\
**Post date:** [January 5, 2022, 8:37pm UTC](https://discuss.kotlinlang.org/t/kotlin-coroutines-stack-trace-problem/23847/2 "2022-01-05T20:37:57Z")

</div>

I have only a limited understanding of coroutines internals, but I believe a continuation keeps the reference to the continuation of the calling function. If this is the case, wouldn’t it be possible to iterate over the continuations chain in order to recreate a call stack? I’m just thinking, probably saying something silly 😉

---

<div class="post-metadata">

**Author:** ![reformator](https://avatars.discourse-cdn.com/v4/letter/r/3ab097/32.png) [@reformator](https://discuss.kotlinlang.org/u/reformator)\
**Post date:** [January 5, 2022, 8:57pm UTC](https://discuss.kotlinlang.org/t/kotlin-coroutines-stack-trace-problem/23847/3 "2022-01-05T20:57:13Z")

</div>

Yeah, you’re right. It’s exactly how Stacktrace-decoroutinator and Kotlin stdlib restore the coroutine call stack.

---

<div class="post-metadata">

**Author:** ![broot](https://avatars.discourse-cdn.com/v4/letter/b/a88e57/32.png) [@broot](https://discuss.kotlinlang.org/u/broot)\
**Post date:** [January 5, 2022, 9:11pm UTC](https://discuss.kotlinlang.org/t/kotlin-coroutines-stack-trace-problem/23847/4 "2022-01-05T21:11:06Z")

</div>

So if there is no problem in re-creating the true call stack then what exactly is the problem? Why do we have to create fake classes, why can’t we just use `setStackTrace()` to fill whatever we need?

I don’t try to argue or suggest that your tool is not needed. I’m honestly curious, because it sounds interesting 🙂

---

<div class="post-metadata">

**Author:** ![reformator](https://avatars.discourse-cdn.com/v4/letter/r/3ab097/32.png) [@reformator](https://discuss.kotlinlang.org/u/reformator)\
**Post date:** [January 5, 2022, 9:24pm UTC](https://discuss.kotlinlang.org/t/kotlin-coroutines-stack-trace-problem/23847/5 "2022-01-05T21:24:01Z")

</div>

Kotlin stdlib does almost as you say. But it doesn’t always work, because it’s not always possible to handle exception creation. For instance, stdlib cannot handle the example from the post.

Stacktrace-decoroutinator bypasses having to handle exception creation because it just creates real call stack at runtime.
