JakeWharton/retrofit2-kotlinx-serialization-converter

Unable to create converter for Retrofit2 Call

abitgen opened this issue · 6 comments

kotlin version - 1.3.70
kotlin Serialization version - 0.20.0

java.lang.IllegalArgumentException: Unable to create converter for retrofit2.Call<T>
        for method _myMethod_
        at retrofit2.Utils.methodError(Utils.java:52)
        at retrofit2.HttpServiceMethod.createResponseConverter(HttpServiceMethod.java:115)
        at retrofit2.HttpServiceMethod.parseAnnotations(HttpServiceMethod.java:82)
        at retrofit2.ServiceMethod.parseAnnotations(ServiceMethod.java:37)
        at retrofit2.Retrofit.loadServiceMethod(Retrofit.java:170)
        at retrofit2.Retrofit$1.invoke(Retrofit.java:149)
        at java.lang.reflect.Proxy.invoke(Proxy.java:813)
        at $Proxy0.createSeamlessSubscription(Unknown Source)
        at _myPackageFile_(ApiHandler.kt:33)
        at _myPackageFile_(AppRepository.kt:10)
        at _myPackageFile_.invokeSuspend(HomeViewModel.kt:24)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:56)
        at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:738)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)
     Caused by: kotlinx.serialization.SerializationException: Can't locate argument-less serializer for class Call. For generic classes, such as lists, please provide serializer explicitly.
        at kotlinx.serialization.PlatformUtilsKt.serializer(PlatformUtils.kt:21)
        at kotlinx.serialization.JvmResolvingKt.serializerByTypeToken(JvmResolving.kt:83)
        at com.jakewharton.retrofit2.converter.kotlinx.serialization.Factory.responseBodyConverter(Factory.kt:23)
        at retrofit2.Retrofit.nextResponseBodyConverter(Retrofit.java:330)
        at retrofit2.Retrofit.responseBodyConverter(Retrofit.java:313)
        at retrofit2.HttpServiceMethod.createResponseConverter(HttpServiceMethod.java:113)
        at retrofit2.HttpServiceMethod.parseAnnotations(HttpServiceMethod.java:82) 
        at retrofit2.ServiceMethod.parseAnnotations(ServiceMethod.java:37) 
        at retrofit2.Retrofit.loadServiceMethod(Retrofit.java:170) 
        at retrofit2.Retrofit$1.invoke(Retrofit.java:149) 
        at java.lang.reflect.Proxy.invoke(Proxy.java:813) 
        at $Proxy0.createSeamlessSubscription(Unknown Source) 
        at io.titan.titan_demo.data.api.ApiHandler.createSeamlessSubscription(ApiHandler.kt:33) 
        at io.titan.titan_demo.repo.AppRepository.createSeamlessSubscription(AppRepository.kt:10) 
        at _myPackageFile_$$inlined$switchMap$1$lambda$1.invokeSuspend(HomeViewModel.kt:24) 
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) 
        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:56) 
        at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571) 
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:738) 
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678) 
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665) ```

Best guess is you have suspend fun foo(): Call<T> which you should replace with suspend fun foo(): T. Without more information it's impossible to tell.

I would like to Call.enqueue and handle http status codes, so I am using Call. Technically, it should work with Call right? I will try downgrading kotlin version.

Please provide the full method declaration.

Api

    @POST("subscriptions")
    suspend fun createSubscription(@Body subscription: SubscriptionData) : Call<SubscriptionResponse>

Response

@Parcelize
@Serializable
data class SubscriptionResponse (

    @SerialName("subReferenceId")
    val subReferenceId: Int

) : Parcelable , BaseResponse()
@Serializable
open class BaseResponse {
    @SerialName("message")
    open var message: String = ""

    @SerialName("status")
    open var status: String = ""
}

Retrofit instance

Retrofit.Builder()
            .baseUrl(API_URL)
            .addConverterFactory(Json.asConverterFactory(MediaType.get("application/json")))
            .build()

In ViewModel

fun create() =
  subscriptionData.switchMap {

            liveData(Dispatchers.IO) {

                if(it!=null) {
                    emit(Resource.loading(data = null))
                    appRepository.createSubscription(it) {
                        this.emit(it)
                    }
                }
        }

AppRepository

createSubscription(subscriptionData: SubscriptionData, call : suspend (Resource<SubscriptionResponse>)-> Unit)

You can use either suspend or Call, not both. If you want the Response<T> you can use it as the return type for your suepend function. If you want to use Call.enqueue you shouldn't use suspend modifier.

Basically: Call and suspend are two implementations of the same idea, the execution mechanism. You have to choose one.

Thank you :)