Ktor: kotlin.native.concurrent.InvalidMutabilityException: mutation attempt of frozen kotlinx.coroutines.StandaloneCoroutine@28a82d8

Created on 7 Jan 2020  路  23Comments  路  Source: ktorio/ktor

works on android, breaks on ios

code:

fun triggerNetworkCall(){
      GlobalScope.apply {
            launch(Background) {
                try {
                    val data = repository.getSettings()
                } catch (t: Throwable) {
                    Logger.d("ktor", t.message?:"null")
                }
            }
        }
}
 suspend fun getSettings(): String? {
        val response =  apiConfig.getReferralData()
        return response
}
suspend fun getReferralData(): String? {
       val localClient = HttpClient(PlatformHttpClient.httpClientEngine){
            install(JsonFeature)
        }
        return localClient.post {
            url {
                protocol = URLProtocol.HTTPS
                host = "postman-echo.com"
                encodedPath = "post"
            }
            contentType(ContentType.Application.Json)
            body = "{}"
            HttpMethod.Post
        }
}

This post request works in android but fails in iOS with following stack trace:

Uncaught Kotlin exception: kotlin.native.concurrent.InvalidMutabilityException: mutation attempt of frozen kotlinx.coroutines.StandaloneCoroutine@1a8b4e8

whereas a get request works on both platforms. Following is the function being used:

suspend fun getReferralData(): String? {
       val localClient = HttpClient(PlatformHttpClient.httpClientEngine){
            install(JsonFeature)
        }

        val address = Url("https://postman-echo.com/get?foo1=bar1&foo2=bar2")
        return localClient.get {
            url {
                url(address.toString())
            }
        }
}

Dispatchers definition:
iOS

internal actual val Main: CoroutineDispatcher = NsQueueDispatcher(dispatch_get_main_queue())

internal actual val Background: CoroutineDispatcher = Main


internal class NsQueueDispatcher(
    private val dispatchQueue: dispatch_queue_t
) : CoroutineDispatcher() {
    override fun dispatch(context: CoroutineContext, block: Runnable) {
        dispatch_async(dispatchQueue) {
            block.run()
        }
    }
}

android

internal actual val Main: CoroutineDispatcher = Dispatchers.Main

internal actual val Background: CoroutineDispatcher = Dispatchers.Default

dependencies

commonMain {

            dependencies {
                implementation "org.jetbrains.kotlin:kotlin-stdlib-common:1.3.61"
                implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:0.14.0"
                implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core-common:1.3.2-1.3.60"
                implementation "co.touchlab:stately:0.9.4"
                implementation "co.touchlab:stately-collections:0.9.4"
                implementation "io.ktor:ktor-client-core:1.2.6"
                implementation "io.ktor:ktor-client-json:1.2.6"
                implementation "io.ktor:ktor-client-logging:1.2.6"
                implementation "io.ktor:ktor-client-serialization:1.2.6"
                implementation "com.github.aakira:napier:1.1.0"

            }
        }

        androidMain {
            dependencies {
                implementation "org.jetbrains.kotlin:kotlin-stdlib:1.3.61"
                implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.14.0"
                implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.2-1.3.60"
                implementation "io.ktor:ktor-client-core-jvm:1.2.6"
                implementation "io.ktor:ktor-client-json-jvm:1.2.6"
                implementation "io.ktor:ktor-client-logging-jvm:1.2.6"
                implementation "io.ktor:ktor-client-serialization-jvm:1.2.6"
                implementation "io.ktor:ktor-client-android:1.2.6"
                implementation "com.github.aakira:napier-android:1.1.0"
            }
        }

        iosMain {
            dependencies {
                implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-native:0.14.0"
                implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core-native:1.3.2-1.3.60"

                implementation "io.ktor:ktor-client-core-native:1.2.6"
                implementation "io.ktor:ktor-client-json-native:1.2.6"
                implementation "io.ktor:ktor-client-logging-native:1.2.6"
                implementation "io.ktor:ktor-client-serialization-native:1.2.6"
                implementation "io.ktor:ktor-client-ios:1.2.6"
                implementation "com.github.aakira:napier-ios:1.1.0"
            }
        }
bug

Most helpful comment

This issue happens on version 1.4.0 while on 1.3.2-1.4.0-rc everything works fine.

All 23 comments

On upgrading to ktor 1.3.0-rc2 get request stopped working too. (on ios)
getting following exception:

Uncaught Kotlin exception: kotlin.native.concurrent.InvalidMutabilityException: mutation attempt of frozen kotlinx.coroutines.InvokeOnCancelling@3a6e688

Please note that on ios you may run ktor client on the main dispatcher. This difference should be avoided at some point but for not it is the only way.

@e5l any update on the same Also we have sent the slack invite please check

@e5l any sample project that has post request captured as well will be helpful

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

This issue happens on version 1.4.0 while on 1.3.2-1.4.0-rc everything works fine.

Having a very similar issue on 1.4.0 when doing a simple get request on ios:

Uncaught Kotlin exception: kotlin.native.concurrent.InvalidMutabilityException: mutation attempt of frozen kotlinx.coroutines.ChildHandleNode@31dca08

reverting back to 1.3.2-1.4.0-rc works like a charm.

Having a very similar issue on 1.4.0 when doing a simple get request on ios:

Uncaught Kotlin exception: kotlin.native.concurrent.InvalidMutabilityException: mutation attempt of frozen kotlinx.coroutines.ChildHandleNode@31dca08

reverting back to 1.3.2-1.4.0-rc works like a charm.

same error with me.

Having a very similar issue on 1.4.0 when doing a simple get request on ios:
Uncaught Kotlin exception: kotlin.native.concurrent.InvalidMutabilityException: mutation attempt of frozen kotlinx.coroutines.ChildHandleNode@31dca08
reverting back to 1.3.2-1.4.0-rc works like a charm.

same error with me.

@joaquim-verges
I solve it.

```gradle.properties
kotlin_version=1.4.0
kotlinx_coroutines_version=1.3.9-native-mt
kotlinx_ktor_version=1.4.0
kotlinx_serialization_version=1.0.0-RC

```build.gradle.kts
// ...
sourceSets {
        val commonMain by sourceSets.getting {
            dependencies {
                api("org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlinx_coroutines_version")
                api("org.jetbrains.kotlinx:kotlinx-serialization-core:$kotlinx_serialization_version")
                api("io.ktor:ktor-client-core:$kotlinx_ktor_version")
                api("io.ktor:ktor-client-json:$kotlinx_ktor_version")
                api("io.ktor:ktor-client-serialization:$kotlinx_ktor_version")
            }
        }

        val iosMain by sourceSets.getting {
            dependencies {
                implementation("io.ktor:ktor-client-ios:$kotlinx_ktor_version")
            }
        }
        val iosX64Main by sourceSets.getting {
            dependencies {
            }
        }
        val iosArm64Main by sourceSets.getting {
            dependencies {
            }
        }
        val androidMain by sourceSets.getting {
            dependencies {
                implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlinx_coroutines_version")
                implementation("io.ktor:ktor-client-android:$kotlinx_ktor_version")
            }
        }
    }

```UserApi.kt

interface IUserApi {
suspend fun users(): List
}

expect val httpClientEngine: HttpClientEngine

class UserApi : IUserApi {

private val baseUrl = "https://jsonplaceholder.typicode.com"
private val client = HttpClient(httpClientEngine) {
    install(JsonFeature) {
        serializer = KotlinxSerializer(json = kotlinx.serialization.json.Json {
            isLenient = false
            ignoreUnknownKeys = true
            allowSpecialFloatingPointValues = true
            useArrayPolymorphism = false
        })
    }
}

override suspend fun users(): List<User> {
    return client.get {
        setupCall(Endpoint.USERS)
    }
}

private fun HttpRequestBuilder.setupCall(endpoint: Endpoint) {
    url {
        takeFrom(urlString = baseUrl)
        encodedPath += endpoint.path
    }
}

}

```iosMain/HttpClientEngine.kt
import io.ktor.client.engine.*
import io.ktor.client.engine.ios.*

actual val httpClientEngine: HttpClientEngine
    get() = Ios.create()

I have the same issue as @joaquim-verges and unfortunately, using kotlinx serialization 1.0.0-RC didn't fix it for me.

Doesn't this mean that KTOR http client 1.4.0 is essentially unusable for native?

Any other ideas?

Edit: I confirm that reverting to 1.3.2-1.4.0-rc also fixes this for me...

Still get the same error (kotlin.native.concurrent.InvalidMutabilityException: mutation attempt of frozen kotlinx.coroutines.ChildHandleNode) with kotlin 1.4.10 and coroutines 1.3.9-native-mt.

I configure the client like this in iOS specific Kodein module:

HttpClient {
            engine {
                Ios.create {
                    configureRequest {
                        setAllowsCellularAccess(true)
                    }
                }
            }
            defaultRequest {
                host = url
            }
            install(JsonFeature) {
                serializer = KotlinxSerializer(
                    Json {
                        isLenient = true
                        ignoreUnknownKeys = true
                        allowSpecialFloatingPointValues = true
                        useArrayPolymorphism = true
                    }
                )
            }
        }

i will try to configure it like @xiaobailong24 did right in the api client class, but i'm not sure it will help

Any updates on this?

I'm also facing the same issue on 1.4.0 and I thought my code was the issue or I misunderstood the variable freezing functionality. But as you all pointed out it is an issue with 1.4.0.

@e5l I understand that you have a huge load of work and that we all go to you when there is an issue especially with Ktor but we would very much appreciate any kind of update on this issue. If there is a workaround or should we revert back to 1.4.0-rc or if the issue is being worked on currently and expected to be fixed in the next version. Any kind of update would be very much appreciated.

Following a hint on here https://youtrack.jetbrains.com/issue/KTOR-915 the exception went away when I removed implementation "io.ktor:ktor-client-logging:$ktor_version"
Obviously not much of a solution, not sure why that issue is marked as resolved.

removing logging feature did not help. Still got the same stack trace:

Uncaught Kotlin exception: kotlin.native.concurrent.InvalidMutabilityException: mutation attempt of frozen kotlinx.coroutines.ChildHandleNode@e8ef88
    at 0   RNInject                            0x0000000103dab76d kfun:kotlin.Throwable#<init>(kotlin.String?){} + 93 (/Users/teamcity/buildAgent/work/cae0e6559deed4c4/runtime/src/main/kotlin/kotlin/Throwable.kt:23:37)
    at 1   RNInject                            0x0000000103da3d5b kfun:kotlin.Exception#<init>(kotlin.String?){} + 91 (/Users/teamcity/buildAgent/work/cae0e6559deed4c4/runtime/src/main/kotlin/kotlin/Exceptions.kt:23:44)
    at 2   RNInject                            0x0000000103da3fab kfun:kotlin.RuntimeException#<init>(kotlin.String?){} + 91 (/Users/teamcity/buildAgent/work/cae0e6559deed4c4/runtime/src/main/kotlin/kotlin/Exceptions.kt:34:44)
    at 3   RNInject                            0x0000000103dde08b kfun:kotlin.native.concurrent.InvalidMutabilityException#<init>(kotlin.String){} + 91 (/Users/teamcity/buildAgent/work/cae0e6559deed4c4/runtime/src/main/kotlin/kotlin/native/concurrent/Freezing.kt:22:60)
    at 4   RNInject                            0x0000000103dde562 ThrowInvalidMutabilityException + 690 (/Users/teamcity/buildAgent/work/cae0e6559deed4c4/runtime/src/main/kotlin/kotlin/native/concurrent/Internal.kt:92:11)
    at 5   RNInject                            0x0000000103ed933c MutationCheck + 108
    at 6   RNInject                            0x00000001040d08f6 kfun:kotlinx.coroutines.internal.LinkedListNode#<set-_next>(kotlinx.coroutines.internal.LinkedListNode){} + 102 (/opt/buildAgent/work/44ec6e850d5c63f0/kotlinx-coroutines-core/native/src/internal/LinkedList.kt:19:28)
    at 7   RNInject                            0x00000001040d0edb kfun:kotlinx.coroutines.internal.LinkedListNode#addLast(kotlinx.coroutines.internal.LinkedListNode){} + 283 (/opt/buildAgent/work/44ec6e850d5c63f0/kotlinx-coroutines-core/native/src/internal/LinkedList.kt:31:9)
    at 8   RNInject                            0x00000001040d1226 kfun:kotlinx.coroutines.internal.LinkedListNode#addOneIfEmpty(kotlinx.coroutines.internal.LinkedListNode){}kotlin.Boolean + 230 (/opt/buildAgent/work/44ec6e850d5c63f0/kotlinx-coroutines-core/native/src/internal/LinkedList.kt:47:9)
    at 9   RNInject                            0x0000000103f6699f kfun:kotlinx.coroutines.JobSupport.promoteSingleToNodeList#internal + 463 (/opt/buildAgent/work/44ec6e850d5c63f0/kotlinx-coroutines-core/common/src/JobSupport.kt:532:15)
    at 10  RNInject                            0x0000000103f655fd kfun:kotlinx.coroutines.JobSupport#invokeOnCompletion(kotlin.Boolean;kotlin.Boolean;kotlin.Function1<kotlin.Throwable?,kotlin.Unit>){}kotlinx.coroutines.DisposableHandle + 2061 (/opt/buildAgent/work/44ec6e850d5c63f0/kotlinx-coroutines-core/common/src/JobSupport.kt:470:25)
    at 11  RNInject                            0x0000000103f64d6f kfun:kotlinx.coroutines.JobSupport#invokeOnCompletion(kotlin.Function1<kotlin.Throwable?,kotlin.Unit>){}kotlinx.coroutines.DisposableHandle + 207 (/opt/buildAgent/work/44ec6e850d5c63f0/kotlinx-coroutines-core/common/src/JobSupport.kt:449:9)
    at 12  RNInject                            0x0000000104424bc4 kfun:io.ktor.client#HttpClient(io.ktor.client.engine.HttpClientEngineFactory<0:0>;kotlin.Function1<io.ktor.client.HttpClientConfig<0:0>,kotlin.Unit>){0搂<io.ktor.client.engine.HttpClientEngineConfig>}io.ktor.client.HttpClient + 1156 (/opt/buildAgent/work/a85294440dc5c6e/ktor-client/ktor-client-core/common/src/io/ktor/client/HttpClient.kt:45:36)
    at 13  RNInject                            0x00000001044b4aae kfun:io.ktor.client#HttpClient(kotlin.Function1<io.ktor.client.HttpClientConfig<*>,kotlin.Unit>){}io.ktor.client.HttpClient + 638 (/opt/buildAgent/work/a85294440dc5c6e/ktor-client/ktor-client-core/posix/src/io/ktor/client/HttpClient.kt:18:46)
    at 14  RNInject                            0x00000001039b0da3 kfun:com.application.inject.networkModule$lambda-13$lambda-12#internal + 931 (/Users/user/AndroidStudioProjects/inject/src/iosMain/kotlin/com/aplication/inject/NetworkModule.kt:49:9)
    at 15  RNInject                            0x00000001039b4043 kfun:com.application.inject.$networkModule$lambda-13$lambda-12$FUNCTION_REFERENCE$43.invoke#internal + 179 (/Users/user/AndroidStudioProjects/inject/src/iosMain/kotlin/com/aplication/inject/NetworkModule.kt:47:38)
    at 16  RNInject                            0x000000010452b272 kfun:org.kodein.di.bindings.Provider.getFactory$lambda-0#internal + 402 (/Users/runner/work/Kodein-DI/Kodein-DI/kodein-di/src/commonMain/kotlin/org/kodein/di/bindings/standardBindings.kt:86:112)
    at 17  RNInject                            0x000000010452b4c0 kfun:org.kodein.di.bindings.Provider.$getFactory$lambda-0$FUNCTION_REFERENCE$81.invoke#internal + 208 (/Users/runner/work/Kodein-DI/Kodein-DI/kodein-di/src/commonMain/kotlin/org/kodein/di/bindings/standardBindings.kt:86:87)
    at 18  RNInject                            0x000000010453f8d1 kfun:org.kodein.di.internal.DIContainerImpl.factory$lambda-2#internal + 241 (/Users/runner/work/Kodein-DI/Kodein-DI/kodein-di/src/commonMain/kotlin/org/kodein/di/internal/DIContainerImpl.kt:160:22)
    at 19  RNInject                            0x000000010454041c kfun:org.kodein.di.internal.DIContainerImpl.$factory$lambda-2$FUNCTION_REFERENCE$98.invoke#internal + 204 (/Users/runner/work/Kodein-DI/Kodein-DI/kodein-di/src/commonMain/kotlin/org/kodein/di/internal/DIContainerImpl.kt:160:20)
    at 20  RNInject                            0x000000010451ef82 kfun:org.kodein.di.DIContainer.provider$<anonymous>_1#internal + 194 (/Users/runner/work/Kodein-DI/Kodein-DI/kodein-di/src/commonMain/kotlin/org/kodein/di/DIContainer.kt:23:1)
    at 21  RNInject                            0x000000010451f38d kfun:org.kodein.di.DIContainer.$provider$<anonymous>_1$FUNCTION_REFERENCE$25.invoke#internal + 157 (/Users/runner/work/Kodein-DI/Kodein-DI/kodein-di/src/commonMain/kotlin/org/kodein/di/DIContainer.kt:22:37)
    at 22  RNInject                            0x0000000104552278 kfun:org.kodein.di.internal.DirectDIBaseImpl#Instance(org.kodein.type.TypeToken<0:0>;kotlin.Any?){0搂<kotlin.Any>}0:0 + 984 (/Users/runner/work/Kodein-DI/Kodein-DI/kodein-di/src/commonMain/kotlin/org/kodein/di/internal/DirectDIImpl.kt:30:153)
    at 23  RNInject                            0x00000001039af117 kfun:com.application.inject.networkModule$lambda-13$lambda-1#internal + 823 (/Users/user/AndroidStudioProjects/inject/src/iosMain/kotlin/com/aplication/inject/NetworkModule.kt:31:44)
    at 24  RNInject                            0x00000001039b3ac3 kfun:com.application.inject.$networkModule$lambda-13$lambda-1$FUNCTION_REFERENCE$39.invoke#internal + 179 (/Users/user/AndroidStudioProjects/inject/src/iosMain/kotlin/com/aplication/inject/NetworkModule.kt:31:44)
    at 25  RNInject                            0x000000010452d152 kfun:org.kodein.di.bindings.Singleton.getFactory$lambda-3$lambda-2$lambda-1#internal + 370 (/Users/runner/work/Kodein-DI/Kodein-DI/kodein-di/src/commonMain/kotlin/org/kodein/di/bindings/standardBindings.kt:127:96)
    at 26  RNInject                            0x000000010452dcd1 kfun:org.kodein.di.bindings.Singleton.$getFactory$lambda-3$lambda-2$lambda-1$FUNCTION_REFERENCE$84.invoke#internal + 161 (/Users/runner/work/Kodein-DI/Kodein-DI/kodein-di/src/commonMain/kotlin/org/kodein/di/bindings/standardBindings.kt:127:67)
    at 27  RNInject                            0x0000000104526617 kfun:org.kodein.di.bindings.Reference.Local.Companion#invoke(kotlin.Function0<0:0>;kotlin.Function1<0:0,0:1>){0搂<kotlin.Any>;1搂<org.kodein.di.bindings.Reference<0:0>>}org.kodein.di.bindings.Reference.Local<0:0,0:1> + 343 (/Users/runner/work/Kodein-DI/Kodein-DI/kodein-di/src/commonMain/kotlin/org/kodein/di/bindings/references.kt:16:127)
    at 28  RNInject                            0x000000010452739a kfun:org.kodein.di.bindings.Strong.Companion#make(kotlin.Function0<0:0>){0搂<kotlin.Any>}org.kodein.di.bindings.Reference.Local<0:0,org.kodein.di.bindings.Strong<0:0>> + 330 (/Users/runner/work/Kodein-DI/Kodein-DI/kodein-di/src/commonMain/kotlin/org/kodein/di/bindings/references.kt:31:97)
    at 29  RNInject                            0x000000010452d367 kfun:org.kodein.di.bindings.Singleton.getFactory$lambda-3$lambda-2#internal + 391 (/Users/runner/work/Kodein-DI/Kodein-DI/kodein-di/src/commonMain/kotlin/org/kodein/di/bindings/standardBindings.kt:127:62)

I was able to solve this as noted in an earlier response by using version 1.3.9-native-mt of kotlinx.coroutines. If you use straight version 1.3.9 version you will get the InvalidMutabilityException.

Fixed with 1.3.9-native-mt-2

Hi, I am using 1.3.9-native-mt-2 but I can't create Ktor-HttpClient in the iOS side. I don't know if I am missing something. My dependencies are:

kotlinVersion = "1.4.0"
ktorVersion = "1.4.0"
coroutineVersion = "1.3.9-native-mt-2"
serializerVersion = "1.0.0-RC"
internal actual val ApplicationDispatcher: CoroutineContext  =
    NsQueueDispatcher(dispatch_get_main_queue())

internal class NsQueueDispatcher(
    private val dispatchQueue: dispatch_queue_t
) : CoroutineDispatcher() {
    override fun dispatch(context: CoroutineContext, block: Runnable) {
        dispatch_async(dispatchQueue) {
            block.run()
        }
    }
}

But when try to create HttpClient this exception is thrown(kotlin.native.concurrent.InvalidMutabilityException: mutation attempt of frozen kotlinx.coroutines.ChildHandleNode):

private val client by lazy {
     HttpClient() {
         install(JsonFeature) {
             serializer = KotlinxSerializer(nonStrictJson)
         }
         install(Logging) {
             logger = Logger.DEFAULT
             level = LogLevel.INFO
         }
     }
}

I'm facing the same issue as @jarroyoesp with one little difference. I'm using Kotlin Version 1.4.10

@jarroyoesp @hamada147
try create HttpClient with Ios HttpClientEngine.
For details, please refer to my example above.

Try creating a new HttpClient every time you use it instead of retaining it in an instance variable. Also disable logging (remove the logging config from the client config).

I got everything working on my side with kotlin 1.4.10, ktor 1.4.0, coroutines 1.3.9-mt-2 and kotlinx.serialization 1.0.0-RC. It even works in a background thread (Dispatchers.DEFAULT). You have to take care though to create a new http client in the background thread for every call and to ensure you don't reference other classes there that are accessed from the main thread.

See the following sample code:

    private suspend fun <T> withHttpClient(perform: suspend HttpClient.() -> T): T {
        // Run the following in a background thread, the HttpClient itself has to be constructed in the background thread as well.
        return withContext(Dispatchers.Default) {
            // Specify the HttpClient configuration,
            // be sure to not capture any global references because they will be frozen otherwise.
            val configuration: HttpClientConfig<*>.() -> Unit = ... 
            val client = HttpClient(block = configuration)
            try {
                perform(client)
            } finally {
                client.close()
            }
        }
    }

    private fun <T: Any>deserialize(type: KClass<T>, bytes: ByteArray): T {
        // Implement any deserialization logic or just use kotlinx.serialization
    }

    /**
     * Sample HTTP get, result will be parsed in the background, but returned in the main thread
     */
    suspend fun <T : Any> get(
        path: String,
        responseType: KClass<T>,
        parameters: Map<String, Any>
    ): T {
        // This happens in the main thread
        return withHttpClient(accessToken) {
            // This happens all in a background thread
            get<ByteArray>(path = path) {
                for ((key, value) in parameters) {
                    parameter(key, value)
                }
            }.let {
                deserialize(responseType, it)
            }
        }
    }

@jarroyoesp @hamada147
try create HttpClient with Ios HttpClientEngine.
For details, please refer to my example above.

Thanks, I tried it but it didn't work. Using this I had HttpClient: mutation attempt of frozen kotlinx.coroutines.JobImpl@37d2078

Worked for me with version 1.3.9-native-mt-2

Was this page helpful?
0 / 5 - 0 ratings