How to handle 204 response in Retrofit using Kotlin Coroutines?
Asked Answered
B

5

54

I'm using Retrofit 2.7.1 with Kotlin coroutines.

I have a Retrofit service defined as such:

@PUT("/users/{userId}.json")
suspend fun updateUserProfile(
        @Path("userId") userId: String,
        @Query("display_name") displayName: String) : Void

This call returns an HTTP 204 No Content response, which causes a crash in Retrofit:

kotlin.KotlinNullPointerException: Response from com.philsoft.test.api.UserApiService.updateUserProfile was null but response body type was declared as non-null
        at retrofit2.KotlinExtensions$await$2$2.onResponse(KotlinExtensions.kt:43)
        at retrofit2.OkHttpCall$1.onResponse(OkHttpCall.java:129)
        at okhttp3.RealCall$AsyncCall.execute(RealCall.java:174)
        at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:919)

How do I handle a 204 response in retrofit, using coroutines, without crashing?

Broth answered 7/1, 2020 at 21:19 Comment(1)
Possibly answered here: #37116864Conlee
C
97

According to this, use Response<Unit> in the retrofit method declaration.

retrofit no content issue

Cleocleobulus answered 7/1, 2020 at 21:45 Comment(3)
I spent almost 2 days on this issue, finally it is working fine with your solution. thank you.Waldman
This is helpful answer!Superorder
If the API can return both null or non-null values, this solution will not work.Euraeurasia
B
20

You can use Response<Unit> to handle 204 responses using Retrofit but when you handle like this Retrofit will not throw an exception for 4xx responses or other exceptional cases.

You may deal with these cases with the following code:

return try {
        val result = apiCall.invoke()
        return if (result is Response<*>) {
            if (result.code() >= 400) {
                // create an error from error body and return
            } else {
                // return success result
            }
        } else {
            // directly return success result
        }
    } catch (t: Throwable) {
        // create an error from throwable and return it!
    }
Beerbohm answered 9/4, 2020 at 17:5 Comment(1)
how to get message data from 205 ?Payton
F
10

Explaining answer of @Dmitri with the sample code:

  1. In your APIInterface, call your API like this,

     @GET(AppConstants.APIEndPoints.GET_NEWS)
     suspend fun getNews(
         @Query("limit") limit: String,
     ): Response<NewsListResponseModel>
    

where, Response is retrofit2.Response

  1. From where you call the API, check for the status code from the function like apiResponse.code().

    val apiResponse = apiEndPointsInterface.getNews(limit)
    if (apiResponse.code() == HttpURLConnection.HTTP_OK) {
         //response success
         ResultOf.Success(apiResponse.body()!!))
     } else {
         //handle your other response codes here
         ResultOf.Failure("No Data Found.", null))
     }
    
Furfuraceous answered 7/1, 2021 at 10:53 Comment(1)
finally it is working fine with your solution. thank you.Hissing
H
6

Using a custom interceptor to change 204's to 200's works for me

class Code204Interceptor : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()
        val response = chain.proceed(request)

        return if (response.code == 204) {
            response.newBuilder().code(200).build()
        } else {
            response
        }
    }
}
Hibernaculum answered 27/2, 2023 at 8:3 Comment(1)
Is this solution ok with error handling?Morpheme
P
3

After almost four years we got a new Retrofit version (2.10.0) which now supports the Unit as Response type. Right from the Changelog:

Support using Unit as a response type. This can be used for non-body HTTP methods like HEAD or body-containing HTTP methods like GET where the body will be discarded without deserialization.

So no more Response<Unit> - just omit the return type.

Pony answered 19/3, 2024 at 13:40 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.