Skip to content

recoverStackTrace causes nested exceptions #3714

@iwismer

Description

@iwismer

Describe the bug

When throwing an exception in a coroutine scope it sometimes becomes nested one level too deep in the enclosing exception.

For example, if I throw a: TestException(IllegalStateException("test")) (a TestException with a cause of IllegalStateException with a message), then the coroutine scope will throw an exception that is: TestException(TestException(IllegalStateException("test"))).

Provide a Reproducer

The following test will fail as a result of this bug:

@Test
fun testNestedException() = runTest {
    val result = runCatching {
        coroutineScope<Unit> {
            throw NestedException(IllegalStateException("ERROR"))
        }
    }
    val ex = result.exceptionOrNull() ?: error("Expected to fail")
    assertTrue(ex is NestedException)
    assertTrue(ex.cause is IllegalStateException)
    assertEquals("ERROR", (ex.cause as IllegalStateException).message)
}

class NestedException : RuntimeException {
    constructor(message: String) : super(message)
    constructor(cause: Throwable) : super(cause)
    constructor() : super()
}

I can get this test to not fail by changing the order of the constructors in the class to:

class NestedException : RuntimeException {
    constructor(cause: Throwable) : super(cause)
    constructor(message: String) : super(message)
    constructor() : super()
}

But I don't think this is actually a good solution as it shouldn't matter the order of the constructors in my exception, it should work the same way regardless.

I think the issue is at this line where the wrong constructor is being selected (the string constructor instead of the cause constructor).

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions