Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stack trace doesn’t point to the origin of API call when throwing an exception with Retrofit

I am using Retrofit, OkHttp, Kotlin coroutines to make HTTP requests.

When the server returns an unsuccessful result (e.g., 404), I throw a custom exception, which is a subtype of IOException.
The resulted stack trace does not point to code that initially made a request.
The resulted stack trace only points to interceptor code, which doesn’t help me identify the original call site.

Question:

How can I ensure that when I throw a custom exception (a subtype of IOException), the stack trace points to code that initially made a request (e.g. catApi.callThatReturns404())?

Constraints:

  • Wrapping every remote API call with try/catch is not an option because the issue is global, and I don’t want to wrap it in hundreds of places.
  • Simply logging inside the interceptor is not sufficient since I need the full stack trace.
  • Avoiding the use of the custom exception is not an option, as it holds information.
  • Solution should also work in production code. Not only when some debug tools are turned on

Example code

fun main(): Unit = runBlocking {
    makeApiCall()
}

private suspend fun makeApiCall() {
    val api = createCatApi()
    api.callThatReturns404()
}

private fun createCatApi(): CatApi {
    val url = "https://cat-fact.herokuapp.com/"
    val httpClient = OkHttpClient.Builder()
        .addInterceptor { chain ->
            val response = chain.proceed(chain.request())
            if (!response.isSuccessful) {
                throw SubTypeOfIOException(response)
            }
            response
        }
        .build()
    val retrofit = Retrofit.Builder()
        .baseUrl(url)
        .addConverterFactory(JacksonConverterFactory.create(ObjectMapper().registerKotlinModule()))
        .client(httpClient)
        .build()
    return retrofit.create<CatApi>()
}

interface CatApi {
    @GET("factss")
    suspend fun callThatReturns404(): Collection<Any>
}

Stack trace, when SubTypeOfIOException is thrown

Exception in thread "main" com.SubTypeOfIOException: 404
    at com.MainKt$createCatApi$$inlined$-addInterceptor$1.intercept(OkHttpClient.kt:1082)

What I want

Exception in thread "main" com.retrofit_test.SubTypeOfIOException: 404
    at com.retrofit_test.Main.makeApiCall(Main.kt:20)
    at com.MainKt$main$1.invokeSuspend(Main.kt:15)
    at com.MainKt.main(Main.kt:14)
Caused by: com.SubTypeOfIOException: 404
    at com.MainKt$createCatApi$$inlined$-addInterceptor$1.intercept(OkHttpClient.kt:1082)

Interesting thing: when I call such code from tests and throw IOException, the stack trace contains information I want. If in tests I throw a subtype of IOException, the stack trace does not contain such information

Versions:

  • org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0
  • com.squareup.retrofit2:retrofit:2.11.0
  • com.squareup.okhttp3:okhttp:4.12.0
like image 288
Geba Avatar asked Nov 18 '25 17:11

Geba


1 Answers

if the lack of frames is related to the coroutines, https://github.com/Anamorphosee/stacktrace-decoroutinator might help.

like image 169
Denis Avatar answered Nov 20 '25 08:11

Denis



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!