title: Android Retrofit cannot parse RequestBody
body: <p>I have trouble with a Retrofit call. I have a node js server which is working fine and I want to send a login request to it from Android.</p>
  <p>The HTTP request expects a JSON request body. I try to create it with OkHttp3.</p>
  <p>Here is my interface code:</p>
  <pre><code>interface ApiService {
       @POST(&quot;/login-player&quot;)
       suspend fun loginPlayer(@Body body: RequestBody): Call&lt;PlayerApiModel&gt;
  }
  </code></pre>
  <p>Of course, I have more paths than this but first of all, I want this to work.</p>
  <p>My model class, if anyone interested:</p>
  <pre><code>data class PlayerApiModel(
      @SerializedName(&quot;player_id&quot;) @Expose var playerId: Int,
      @SerializedName(&quot;_id&quot;) @Expose var id: String,
      @SerializedName(&quot;player_name&quot;) @Expose var name: String,
      @SerializedName(&quot;password_hash&quot;) @Expose var password: String,
      @SerializedName(&quot;_v&quot;) @Expose var version: Int
  )
  </code></pre>
  <p>This is exactly the same schema as I get from the node server. But the problem is the request cannot even reach the server.</p>
  <p>My Retrofit singleton looks like this:</p>
  <pre><code>class RetrofitInstance {
      companion object {
          private const val URL = &quot;https://pedro.sch.bme.hu/&quot;
  
          val retrofit: ApiService by lazy {
              val interceptor = HttpLoggingInterceptor()
              val httpClient = OkHttpClient.Builder()
                  .addInterceptor(interceptor)
                  .connectTimeout(30, TimeUnit.SECONDS)
                  .readTimeout(30, TimeUnit.SECONDS)
                  .followRedirects(true)
                  .followSslRedirects(true)
                  .addInterceptor { chain -&gt;
                      val newRequest = chain.request().newBuilder()
                          .addHeader(&quot;Authorization&quot;, UUID.randomUUID().toString())
                          .build()
                      chain.proceed(newRequest)
                  }
  
              val builder = Retrofit.Builder()
                  .baseUrl(URL)
                  .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                  .addCallAdapterFactory(SimpleCallAdapterFactory.create())
                  .addConverterFactory(ScalarsConverterFactory.create())
                  .addConverterFactory(GsonConverterFactory.create())
                  .addConverterFactory(MoshiConverterFactory.create())
  
              val retrofit = builder
                  .client(httpClient.build())
                  .build()
              retrofit.create(ApiService::class.java)
          }
      }
  }
  </code></pre>
  <p>And lastly here is the problematic code part: the call.</p>
  <pre><code>val playerName = bind.root.txtPlayerName.text.toString()
  val password = bind.root.txtPassword.text.toString()
  
  val jsonObject = JSONObject()
  jsonObject.put(&quot;player_name&quot;, playerName)
  jsonObject.put(&quot;password&quot;, password)
  val jsonBody = RequestBody.create(MediaType.parse(&quot;application/json; charset=utf-8&quot;), jsonObject.toString())
  
  RetrofitInstance.retrofit.loginPlayer(jsonBody)
      .enqueue(object : Callback&lt;PlayerApiModel&gt; {
          override fun onResponse(call: Call&lt;PlayerApiModel&gt;, response: Response&lt;PlayerApiModel&gt;) {
              println(&quot;${response.code()}: ${response.message()}&quot;)
              when (response.code()) {
                  200 -&gt; {
                      listener.goToMenu(playerName)
                  }
                  400 -&gt; {
                      enableEditTexts()
                      Snackbar.make(bind.root, response.message(), Snackbar.LENGTH_LONG).show()
                  }
                  500 -&gt; {
                      enableEditTexts()
                      serverErrorSnackbar()
                  }
              }
          }
  
          override fun onFailure(call: Call&lt;PlayerApiModel&gt;, t: Throwable) {
              Log.i(&quot;LoginViewModel::login()&quot;, t.message)
              enableEditTexts()
          }
      })
  </code></pre>
  <p>The exception that I get comes to the loginPlayer() call.</p>
  <p>This is the complete stack trace:</p>
  <pre><code>E/AndroidRuntime: FATAL EXCEPTION: DefaultDispatcher-worker-1
      Process: neptun.jxy1vz.cluedo, PID: 14287
      java.lang.IllegalArgumentException: Unable to create call adapter for class java.lang.Object
          for method ApiService.loginPlayer
          at retrofit2.ServiceMethod$Builder.methodError(ServiceMethod.java:755)
          at retrofit2.ServiceMethod$Builder.createCallAdapter(ServiceMethod.java:240)
          at retrofit2.ServiceMethod$Builder.build(ServiceMethod.java:165)
          at retrofit2.Retrofit.loadServiceMethod(Retrofit.java:170)
          at retrofit2.Retrofit$1.invoke(Retrofit.java:147)
          at java.lang.reflect.Proxy.invoke(Proxy.java:1006)
          at $Proxy1.loginPlayer(Unknown Source)
          at neptun.jxy1vz.cluedo.ui.activity.login.LoginViewModel$login$1.invokeSuspend(LoginViewModel.kt:57)
          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: java.lang.IllegalArgumentException: Could not locate call adapter for class java.lang.Object.
        Tried:
         * retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
         * neptun.jxy1vz.cluedo.network.call_adapter.SimpleCallAdapterFactory
         * retrofit2.ExecutorCallAdapterFactory
          at retrofit2.Retrofit.nextCallAdapter(Retrofit.java:241)
          at retrofit2.Retrofit.callAdapter(Retrofit.java:205)
          at retrofit2.ServiceMethod$Builder.createCallAdapter(ServiceMethod.java:238)
              ... 12 more
  I/Process: Sending signal. PID: 14287 SIG: 9
  </code></pre>
  <p><strong>Update:</strong>
  I got a suggestion to remove RxJava2CallAdapterFactory from Retrofit.</p>
  <p>Now the bottom of the stack trace is this:</p>
  <pre><code>Tried:
         * neptun.jxy1vz.cluedo.network.call_adapter.SimpleCallAdapterFactory
         * retrofit2.ExecutorCallAdapterFactory
          at retrofit2.Retrofit.nextCallAdapter(Retrofit.java:241)
          at retrofit2.Retrofit.callAdapter(Retrofit.java:205)
          at retrofit2.ServiceMethod$Builder.createCallAdapter(ServiceMethod.java:238)
              ... 12 more
  </code></pre>
  <p>You can see here my SimpleCallAdapter, which is pretty simple (haha), I wrote this according to a tutorial, but I did not do any special thing in it:</p>
  <pre><code>class SimpleCallAdapterFactory private constructor() : CallAdapter.Factory() {
  
      override fun get(returnType: Type?, annotations: Array&lt;out Annotation&gt;?, retrofit: Retrofit?): CallAdapter&lt;*, *&gt;? =
          returnType?.let {
              return try {
                  // get enclosing type
                  val enclosingType = (it as ParameterizedType)
  
                  // ensure enclosing type is 'Simple'
                  if (enclosingType.rawType != Simple::class.java)
                      null
                  else {
                      val type = enclosingType.actualTypeArguments[0]
                      SimpleCallAdapter&lt;Any&gt;(type)
                  }
              } catch (ex: ClassCastException) {
                  null
              }        }
  
      companion object {
          @JvmStatic
          fun create() = SimpleCallAdapterFactory()
      }
  
  }
  
  class SimpleCallAdapter&lt;R&gt;(private val responseType: Type): CallAdapter&lt;R, Any&gt; {
  
      override fun responseType(): Type = responseType
  
      override fun adapt(call: Call&lt;R&gt;): Any = Simple(call)
  }
  
  class Simple&lt;R&gt;(private val call: Call&lt;R&gt;) {
      fun run(responseHandler: (R?, Throwable?) -&gt; Unit) {
          // run in the same thread
          try {
              // call and handle response
              val response = call.execute()
              handleResponse(response, responseHandler)
  
          } catch (t: IOException) {
              responseHandler(null, t)
          }
      }
  
      fun process(responseHandler: (R?, Throwable?) -&gt; Unit) {
          // define callback
          val callback = object : Callback&lt;R&gt; {
  
              override fun onFailure(call: Call&lt;R&gt;?, t: Throwable?) =
                  responseHandler(null, t)
  
              override fun onResponse(call: Call&lt;R&gt;?, r: Response&lt;R&gt;?) =
                  handleResponse(r, responseHandler)
          }
  
          // enqueue network call
          call.enqueue(callback)
      }
  
      private fun handleResponse(response: Response&lt;R&gt;?, handler: (R?, Throwable?) -&gt; Unit) {
          response?.let {
              if (response.isSuccessful) {
                  handler(response.body(), null)
                  println(response.body())
              }
              else {
                  println(&quot;${response.code()}: ${response.message()}&quot;)
                  if (response.code() in 400..511)
                      handler(null, HttpException(response))
                  else
                      handler(response.body(), null)
              }
          }
      }
  }
  </code></pre>
  <p>And the usage of this:</p>
  <pre><code>RetrofitInstance.retrofit.loginPlayer(jsonBody).process { playerApiModel, throwable -&gt;
      println(&quot;Debug: ${playerApiModel?.name}&quot;)
  }
  </code></pre>
  <p>And in the interface:</p>
  <pre><code>interface ApiService {
       @POST(&quot;/login-player&quot;)
       suspend fun loginPlayer(@Body body: RequestBody): Simple&lt;PlayerApiModel&gt;
  }
  </code></pre>
  <p><strong>End of update</strong></p>
  <p>Curious that it does not recognize the RequestBody type and instead it tries to pass java.lang.Object.</p>
  <p>I have all the necessary dependencies in Gradle.</p>