Stack overflows with Logback and Kotlin Coroutines
Quite recently, when we changed some of our code to use CouroutineScope.async()
instead of GlobalScope.async()
When doing this, we came across an interesting exception that was thrown.

This was the offending code, simplified for the sake of demonstration:
thatCallsTheLastFunction()
throws the exception above.
Weird.
If you were to have a cursory look at the stack-trace in the exception, you might guess logback
might be the culprit.
And it indeed is: https://jira.qos.ch/browse/LOGBACK-102 7.
What is happening is that logback will not be able to print out an exception, if it were to have a suppressedException
that is a circular reference to the exception itself.
But why did that happen in the first place? To understand that, we must open up the documentation to the CoroutineScope
and see how exceptions are aggregated in a CoroutineScope
when it is thrown from different children from within it.
When the CoroutineScope
creates the final exception that is to be returned to the caller, it would consider the first exception that was thrown to be the actual exception, and all the exceptions thrown after that to be a supressedException.
In the example above the suppressedException would be the rethrown exception in Line 11. However, consider that we are throwing the first exception as a cause. This is what creates the circular reference.

Solving the problem
In our particular case, the solution was simple. Move the rethrowing catch
block outside the CoroutineScope
This solved our problem, as now the suppressingException
is no longer populated by the CoroutineScope
, and hence the circular reference was removed.
Links
- Why use Structured Concurrency? https://medium.com/@elizarov/structured-concurrency-722d765aa952