Search code examples
androidkotlinretrofit2okhttp

Retrofit crashes when I throw an exception in an interceptor


I have an interceptor that handles some authentication things. If the authentication fails it throws and Exception. According to what I could find throwing an exception should result in onFailure getting called where I handle the exception. Unfortunatly this does not happen and the app simply crashes completely. I am sure I must be missing something but I cant seem to figure it out. Hope someone here can help :)

Code and stack trace below:

val client = OkHttpClient.Builder()
     // Add interceptor that throws
     .addInterceptor { chain ->
         throw Exception("test")
     }
     .build()

retrofit = Retrofit.Builder()
                .baseUrl(baseURL)
                .client(client)
                .build()

api = retrofit.create(ApiInterface::class.java)


// Create api call...

apicall.enqueue(object : Callback<T> {
    override fun onResponse(call: Call<T>?, response: retrofit2.Response<T>?) {
        // ....
    }

    override fun onFailure(call: Call<T>?, t: Throwable?) {
        // Expect to go here to handle the Exception. But the app crashes
    }
})

Stack trace:

E/AndroidRuntime: FATAL EXCEPTION: OkHttp Dispatcher
                  Process: com.testapp.test.debug, PID: 28891
                  java.lang.Error: java.lang.Exception: test
                      at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1168)
                      at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
                      at java.lang.Thread.run(Thread.java:764)
                   Caused by: java.lang.Exception: test
                      at com.testapp.test.data.networking.RetrofitWebApi$client$1.intercept(RetrofitWebApi.kt:90)
                      at com.testapp.test.data.networking.RetrofitWebApi$client$1.intercept(RetrofitWebApi.kt:76)
                      at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
                      at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
                      at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:185)
                      at okhttp3.RealCall$AsyncCall.execute(RealCall.java:135)
                      at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32)
                      at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
                      at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636) 
                      at java.lang.Thread.run(Thread.java:764) 

Solution

  • OkHttp will only catch the exception that happens in the Interceptor if it's an IOException. You can see the code that does this here, the relevant parts (simplified) are as below:

    try {
      Response response = getResponseWithInterceptorChain();
    } catch (IOException e) {
      responseCallback.onFailure(RealCall.this, e);
    }
    

    So if you change your code to the following, you'll get a callback in your onFailure method as expected:

    val client = OkHttpClient.Builder()
         // Add interceptor that throws
         .addInterceptor { chain ->
             throw IOException("test")
         }
         .build()