Search code examples
androidkotlinretrofitkotlin-coroutinesillegalargumentexception

Another case with Exception: java.lang.IllegalArgumentException: No Retrofit annotation found. (parameter #2) in ApiService.login


I'm stuck at Exception that appears in the title. I checked similar topics, but they were specific cases that didn't apply to my situation. Below you could see my models, retrofit setup and its usage. I tried removing Body Class, Response Class, just to check if they are culprits, but unfortunately those weren't the case. Maybe someone will be able to figure out what I'm doing wrong?

Stack Trace:

java.lang.IllegalArgumentException: No Retrofit annotation found. (parameter #2)
        for method ApiService.login
        at retrofit2.Utils.methodError(Utils.java:52)
        at retrofit2.Utils.methodError(Utils.java:42)
        at retrofit2.Utils.parameterError(Utils.java:61)
        at retrofit2.RequestFactory$Builder.parseParameter(RequestFactory.java:311)
        at retrofit2.RequestFactory$Builder.build(RequestFactory.java:182)
        at retrofit2.RequestFactory.parseAnnotations(RequestFactory.java:65)
        at retrofit2.ServiceMethod.parseAnnotations(ServiceMethod.java:25)
        at retrofit2.Retrofit.loadServiceMethod(Retrofit.java:168)
        at retrofit2.Retrofit$1.invoke(Retrofit.java:147)
        at java.lang.reflect.Proxy.invoke(Proxy.java:1006)
        at $Proxy1.login(Unknown Source)
        at com.rudearts.cyber2020.services.NetworkService$login$1.invokeSuspend(NetworkService.kt:17)
        at com.rudearts.cyber2020.services.NetworkService$login$1.invoke(Unknown Source:10)
        at kotlinx.coroutines.flow.SafeFlow.collect(Builders.kt:56)
        at kotlinx.coroutines.flow.internal.ChannelFlowOperatorImpl.flowCollect(ChannelFlow.kt:144)
        at kotlinx.coroutines.flow.internal.ChannelFlowOperator.collectTo$suspendImpl(ChannelFlow.kt:111)
        at kotlinx.coroutines.flow.internal.ChannelFlowOperator.collectTo(Unknown Source:0)
        at kotlinx.coroutines.flow.internal.ChannelFlow$collectToFun$1.invokeSuspend(ChannelFlow.kt:33)
        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)

Models:

import com.google.gson.annotations.SerializedName

data class LoginRequest(
    @SerializedName("pin")  val pin:String,
    @SerializedName("pushId") val pushId:String)
import com.google.gson.annotations.SerializedName

data class UserJson (

    @SerializedName("id") val id:Long,
    @SerializedName("name") val name:String?,
    @SerializedName("access_rights") val accessRights:String?)

Retrofit Builder

import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory

object RetrofitBuilder {

    private const val BASE_URL = "<url>"

    private val client = OkHttpClient.Builder().build()

    private fun getRetrofit(): Retrofit {
        return Retrofit.Builder()
            .baseUrl(BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .client(client)
            .build()
    }

    val apiService: ApiService = getRetrofit().create(ApiService::class.java)
}
import com.rudearts.cyber2020.model.LoginRequest
import com.rudearts.cyber2020.model.UserJson
import retrofit2.Response
import retrofit2.http.Body
import retrofit2.http.Headers
import retrofit2.http.POST

interface ApiService {

    @Headers("Content-Type: application/json")
    @POST("login.php")
    suspend fun login(@Body request: LoginRequest):Response<UserJson>

}

Usage in other class:

import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import java.util.*

class NetworkService {

    private val repoService by lazy { RepoService.instance }
    private val apiService = RetrofitBuilder.apiService

    fun login(pin:String, token:String): Flow<NetworkResult<Boolean>> = flow {
        emit(Loading)
        try {
            val userJson = apiService.login(LoginRequest(pin,token)).body()
            userJson?.let {
                val user =
                    User(userJson.id, userJson.name ?: "", emptyList(), userJson.accessRights ?: "")
                repoService.user = user
            }

            emit(NetworkSuccess(userJson != null))
        } catch (throwable: Throwable) {
            emit(NetworkError(throwable))
        }
    }
}

Solution

  • It could be that you are using an older version of okhttp / retrofit as you are using suspend function it requires the latest version of both libraries.