Retrofit 2.6.0 exception: java.lang.IllegalArgumentException: Unable to create call adapter for kotlinx.coroutines.Deferred
Asked Answered
O

3

30

I have a project with Kotlin coroutines and Retrofit.

I had these dependencies:

implementation 'com.squareup.retrofit2:retrofit:2.5.0'
implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2'

Today I have updated Retrofit to 2.6.0 in the project. In https://github.com/JakeWharton/retrofit2-kotlin-coroutines-adapter it is written that it is deprecated now. In https://github.com/square/retrofit/blob/master/CHANGELOG.md#version-260-2019-06-05 it is written that Retrofit currently supports suspend.

So, I removed retrofit2-kotlin-coroutines-adapter:0.9.2 and in Retrofit client changed these lines:

        retrofit = Retrofit.Builder()
            .baseUrl(SERVER_URL)
            .client(okHttpClient)
            .addConverterFactory(MyGsonFactory.create(gson))
            //.addCallAdapterFactory(CoroutineCallAdapterFactory()) - removed it.
            .build()

When run, the first request catches an exception:

java.lang.IllegalArgumentException: Unable to create call adapter for kotlinx.coroutines.Deferred<com.package.model.response.UserInfoResponse>
    for method Api.getUserInfo

As I understood, instead of CoroutineCallAdapterFactory() I could use CallAdapter.Factory(), but it is abstract.

If in Api class I change a request adding suspend in the beginning:

@FormUrlEncoded
@POST("user/info/")
suspend fun getUserInfo(@Field("token") token: String): Deferred<UserInfoResponse>

override suspend fun getUserInfo(token: String): Deferred<UserInfoResponse> =
    service.getUserInfo(token)

I get this exception:

java.lang.RuntimeException: Unable to invoke no-args constructor for kotlinx.coroutines.Deferred<com.package.model.response.UserInfoResponse>. Registering an InstanceCreator with Gson for this type may fix this problem.
Oxidize answered 6/6, 2019 at 8:21 Comment(0)
O
35

Reading https://github.com/square/retrofit/blob/master/CHANGELOG.md#version-260-2019-06-05 I saw:

New: Support suspend modifier on functions for Kotlin! This allows you to express the asynchrony of HTTP requests in an idiomatic fashion for the language.

@GET("users/{id}") suspend fun user(@Path("id") long id): User

Behind the scenes this behaves as if defined as fun user(...): Call and then invoked with Call.enqueue. You can also return Response for access to the response metadata.

Currently this integration only supports non-null response body types. Follow issue 3075 for nullable type support.

I changed requests so: added suspend and removed Deferred:

@FormUrlEncoded
@POST("user/info/")
suspend fun getUserInfo(@Field("token") token: String): UserInfoResponse


override suspend fun getUserInfo(token: String): UserInfoResponse =
    service.getUserInfo(token)

Then in interactor (or simply when called the method getUserInfo(token)) removed await():

override suspend fun getUserInfo(token: String): UserInfoResponse =
    // api.getUserInfo(token).await() - was before.
    api.getUserInfo(token)

UPDATE

Once I encountered a situation when downloading PDF files required removing suspend in Api class. See How to download PDF file with Retrofit and Kotlin coroutines?.

Oxidize answered 6/6, 2019 at 8:45 Comment(6)
My answer was short and on point but your answer is complete with sources and everything. You can accept your own answer.Lowis
@EugenPechanec, nevertheless, you made a nice work and wasted time, and you were the first. At least, you deserve +10.Oxidize
I don't do it for the points, it's fine. I appreciate your gesture.Lowis
Thank you @CoolMind. You saved my bacon!Sot
@Eugen Pechanec Where is your answer?Woodberry
@TheincredibleJan Hi, I deleted it because this answer is essentially the same but more complete.Lowis
N
16

In my case I was missing the CoroutineCallAdapterFactory in my Retrofit initialization. Retrofit v2.5.0

Before:

val retrofit = Retrofit.Builder()
                .baseUrl(BuildConfig.BASE_URL)
                .client(httpClient)
                .addConverterFactory(MoshiConverterFactory.create())
                .build()

After: (working code)

val retrofit = Retrofit.Builder()
                .baseUrl(BuildConfig.BASE_URL)
                .client(httpClient)
                .addConverterFactory(MoshiConverterFactory.create())
                .addCallAdapterFactory(CoroutineCallAdapterFactory())
                .build()
Neumark answered 2/7, 2019 at 13:23 Comment(6)
Well, what version of Retrofit do you use?Oxidize
@Oxidize implementation 'com.squareup.retrofit2:retrofit:2.5.0'Neumark
Ah, I see. Then, it makes sense. But I advise to upgrade to 2.6.0. Requests make simplier.Oxidize
@Oxidize by the way in your question there's 2.5.0 version ;). In any case I just tested my code and it works for 2.6.0 version tooNeumark
Hah, I was upgrading from 2.5.0 to 2.6.0 and caught that exception. Currently it is Ok. Surprised that it works for 2.6.0.Oxidize
Addition of Code .addCallAdapterFactory(CoroutineCallAdapterFactory()) helpedHugohugon
N
0

This error may persist even after adding the suspend keyword to your method. It may happen that calls with suspend functions are not supported by your Retrofit version. Upgrade your Retrofit version to 2.6.0 or greater for suspend functions support.

Nobleminded answered 21/12, 2023 at 15:7 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.