Ktor: Windows Curl invalid mutability exception

Created on 24 Apr 2020  路  3Comments  路  Source: ktorio/ktor

Ktor Version and Engine Used (client or server and name)
OS: Windows 10 (64 bit)
Client version: CURL (msys64)
ktor version: 1.3.2
Kotlin version: 1.3.72

Describe the bug
Sending a request that is not successful (status code != 200) throws InvalidMutabilityException

To Reproduce

    val client = HttpClient(Curl)
    client.get<String>("https://any-url-that-returns-status-code-not-200.com")

Expected behavior
Code throws ClientRequestException, like it does on JVM

Screenshots

Uncaught Kotlin exception: kotlin.native.concurrent.InvalidMutabilityException: mutation attempt of frozen kotlin.collections.HashMap@1240b58
        at kfun:kotlin.Throwable.<init>(kotlin.String?;kotlin.Throwable?)kotlin.Throwable (000000000045e120)
        at kfun:kotlin.Throwable.<init>(kotlin.String?)kotlin.Throwable (000000000045e410)
        at kfun:kotlin.Exception.<init>(kotlin.String?)kotlin.Exception (0000000000457800)
        at kfun:kotlin.RuntimeException.<init>(kotlin.String?)kotlin.RuntimeException (0000000000457340)
        at kfun:kotlin.native.concurrent.InvalidMutabilityException.<init>(kotlin.String)kotlin.native.concurrent.InvalidMutabilityException (00000000004848e0)
        at ThrowInvalidMutabilityException (0000000000485d50)
        at MutationCheck (00000000008b3800)
        at kfun:kotlin.collections.HashMap.<set-length>#internal (0000000000468fc0)
        at kfun:kotlin.collections.HashMap.addKey$stdlib(K)kotlin.Int (000000000046cea0)
        at kfun:kotlin.collections.HashMap.put(K;V)V? (000000000046a000)
        at kfun:io.ktor.util.AttributesNative.put#internal (0000000000660e50)
        at kfun:io.ktor.client.features.$addDefaultResponseValidation$lambda-1$lambda-0COROUTINE$174.invokeSuspend#internal (00000000006b6db0)
        at kfun:io.ktor.client.features.$addDefaultResponseValidation$lambda-1$lambda-0COROUTINE$174.invoke#internal (00000000006b7980)
        at kfun:io.ktor.client.features.HttpCallValidator.$validateResponseCOROUTINE$180.invokeSuspend#internal (00000000006bfb00)
        at kfun:io.ktor.client.features.HttpCallValidator.validateResponse#internal (00000000006c0210)
        at kfun:io.ktor.client.features.HttpCallValidator.Companion.$install$lambda-1COROUTINE$179.invokeSuspend#internal (00000000006c2800)
        at kfun:io.ktor.client.features.HttpCallValidator.Companion.$install$lambda-1COROUTINE$179.invoke#internal (00000000006c3430)
        at kfun:io.ktor.util.pipeline.SuspendFunctionGun.loop#internal (000000000065d4e0)
        at kfun:io.ktor.util.pipeline.SuspendFunctionGun.proceed#internal (000000000065cf50)
        at kfun:io.ktor.util.pipeline.SuspendFunctionGun.execute#internal (000000000065d2a0)
        at kfun:io.ktor.util.pipeline.Pipeline.execute(TContext;TSubject)TSubject (00000000006583d0)
        at kfun:io.ktor.client.call.HttpClientCall.$receiveCOROUTINE$169.invokeSuspend(kotlin.Result<kotlin.Any?>)kotlin.Any? (00000000006aa240)
        at kfun:io.ktor.client.call.HttpClientCall.receive(io.ktor.client.call.TypeInfo)kotlin.Any (00000000006aaf50)
        at kfun:sample.$main$lambda-0COROUTINE$0.invokeSuspend#internal (000000000084a030)
        at kfun:kotlin.coroutines.native.internal.BaseContinuationImpl.resumeWith(kotlin.Result<kotlin.Any?>) (0000000000478be0)
        at kfun:io.ktor.util.pipeline.SuspendFunctionGun.resumeRootWith#internal (000000000065dc30)
        at kfun:io.ktor.util.pipeline.SuspendFunctionGun.loop#internal (000000000065d4e0)
        at kfun:io.ktor.util.pipeline.SuspendFunctionGun.object-1.resumeWith#internal (000000000065f700)
        at kfun:kotlin.coroutines.native.internal.BaseContinuationImpl.resumeWith(kotlin.Result<kotlin.Any?>) (0000000000478be0)
        at kfun:io.ktor.util.pipeline.SuspendFunctionGun.resumeRootWith#internal (000000000065dc30)
        at kfun:io.ktor.util.pipeline.SuspendFunctionGun.loop#internal (000000000065d4e0)
        at kfun:io.ktor.util.pipeline.SuspendFunctionGun.object-1.resumeWith#internal (000000000065f700)
        at kfun:kotlin.coroutines.native.internal.BaseContinuationImpl.resumeWith(kotlin.Result<kotlin.Any?>) (0000000000478be0)
        at kfun:io.ktor.util.pipeline.SuspendFunctionGun.resumeRootWith#internal (000000000065dc30)
        at kfun:io.ktor.util.pipeline.SuspendFunctionGun.loop#internal (000000000065d4e0)
        at kfun:io.ktor.util.pipeline.SuspendFunctionGun.object-1.resumeWith#internal (000000000065f700)
        at kfun:kotlin.coroutines.native.internal.BaseContinuationImpl.resumeWith(kotlin.Result<kotlin.Any?>) (0000000000478be0)
        at kfun:io.ktor.util.pipeline.SuspendFunctionGun.resumeRootWith#internal (000000000065dc30)
        at kfun:io.ktor.util.pipeline.SuspendFunctionGun.loop#internal (000000000065d4e0)
        at kfun:io.ktor.util.pipeline.SuspendFunctionGun.object-1.resumeWith#internal (000000000065f700)
        at kfun:kotlin.coroutines.native.internal.BaseContinuationImpl.resumeWith(kotlin.Result<kotlin.Any?>) (0000000000478be0)
        at kfun:kotlinx.coroutines.DispatchedTask.run() (000000000057ee30)
        at kfun:kotlinx.coroutines.EventLoopImplBase.processNextEvent()kotlin.Long (000000000053d3f0)
        at kfun:kotlinx.coroutines.BlockingCoroutine.joinBlocking#internal (000000000058cd40)
        at kfun:kotlinx.coroutines.runBlocking(kotlin.coroutines.CoroutineContext;kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.CoroutineScope,T>){0<kotlin.Any?>}Generic (000000000058c130)
        at kfun:kotlinx.coroutines.runBlocking$default(kotlin.coroutines.CoroutineContext?;kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.CoroutineScope,T>;kotlin.Int){0<kotlin.Any?>}Generic (000000000058c9f0)
        at kfun:sample.main() (0000000000849e60)
        at Konan_start (000000000084cc10)
        at Init_and_run_start (00000000008920c0)
        at __tmainCRTStartup (0000000000401180)
        at mainCRTStartup (00000000004014f0)
        at  (00007fffccf67bc0)
        at  (00007fffcd22ce30)
bug

Most helpful comment

Hi :)
I麓m experiencing the same on macOS and Linux.


I was able to hotfix it by configuring expectSuccess = false in the client configuration and validating the response on my own:

val client = HttpClient {
  expectSuccess = false
}
val response = request<HttpResponse> {
  /* ... */
}

response.status.isSuccess()
/* ... */

Ok, it seems you don麓t need expectSuccess = false if you get the whole HttpResponse.


Added my own simple validation:

HttpClient {
  HttpResponseValidator {
      validateResponse { response ->
          when (response.status.value) {
              in 300..399 -> throw RedirectResponseException(response)
              in 400..499 -> throw ClientRequestException(response)
              in 500..599 -> throw ServerResponseException(response)
          }
      }
  }

  expectSuccess = false
}

All 3 comments

Hi :)
I麓m experiencing the same on macOS and Linux.


I was able to hotfix it by configuring expectSuccess = false in the client configuration and validating the response on my own:

val client = HttpClient {
  expectSuccess = false
}
val response = request<HttpResponse> {
  /* ... */
}

response.status.isSuccess()
/* ... */

Ok, it seems you don麓t need expectSuccess = false if you get the whole HttpResponse.


Added my own simple validation:

HttpClient {
  HttpResponseValidator {
      validateResponse { response ->
          when (response.status.value) {
              in 300..399 -> throw RedirectResponseException(response)
              in 400..499 -> throw ClientRequestException(response)
              in 500..599 -> throw ServerResponseException(response)
          }
      }
  }

  expectSuccess = false
}

Please check the following ticket on YouTrack for follow-ups to this issue. GitHub issues will be closed in the coming weeks.

Fixed in master

Was this page helpful?
0 / 5 - 0 ratings