Description
Describe the bug
I'm not certain but I think there is a problem with Coil and the way it acts when the image loading is cancelled by AsyncImage
moving out of scope, specifically on wasmJs. Using the circuit navigation library, when changing the Screen
(effectively the @Composable
) from a LaunchedEffect
while the current AsyncImage
is still loading, this exception occurs (app/website crash):
Uncaught runtime errors:
ERROR
The operation was aborted.
io.ktor.client.fetch.abort_$external_fun@webpack-internal:///./kotlin/composeApp.uninstantiated.mjs:4645:73
<CoilBug:composeApp>.io.ktor.client.engine.js.JsClientEngine$execute$lambda.invoke@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[59948]:0x761a81
<CoilBug:composeApp>.io.ktor.client.engine.js.JsClientEngine$execute$lambda.invoke@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[59949]:0x761aa9
<CoilBug:composeApp>.kotlinx.coroutines.InvokeOnCancelling.invoke@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[15857]:0x3c6146
<CoilBug:composeApp>.kotlinx.coroutines.JobSupport.notifyCancelling@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[15773]:0x3c33df
<CoilBug:composeApp>.kotlinx.coroutines.JobSupport.tryMakeCompletingSlowPath@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[15816]:0x3c53e1
<CoilBug:composeApp>.kotlinx.coroutines.JobSupport.tryMakeCompleting@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[15815]:0x3c5202
<CoilBug:composeApp>.kotlinx.coroutines.JobSupport.cancelMakeCompleting@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[15806]:0x3c495c
<CoilBug:composeApp>.kotlinx.coroutines.JobSupport.cancelImpl@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[15805]:0x3c4847
<CoilBug:composeApp>.kotlinx.coroutines.JobSupport.cancelInternal@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[15801]:0x3c47c1
<CoilBug:composeApp>.kotlinx.coroutines.JobSupport.cancel@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[15798]:0x3c46ed
<CoilBug:composeApp>.io.ktor.client.engine.createCallContext$lambda.invoke@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[58743]:0x749a64
<CoilBug:composeApp>.io.ktor.client.engine.createCallContext$lambda.invoke@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[58744]:0x749a8d
<CoilBug:composeApp>.kotlinx.coroutines.InvokeOnCancelling.invoke@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[15857]:0x3c6146
<CoilBug:composeApp>.kotlinx.coroutines.JobSupport.notifyCancelling@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[15773]:0x3c33df
<CoilBug:composeApp>.kotlinx.coroutines.JobSupport.tryMakeCancelling@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[15812]:0x3c4fed
<CoilBug:composeApp>.kotlinx.coroutines.JobSupport.makeCancelling@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[15810]:0x3c4e5b
<CoilBug:composeApp>.kotlinx.coroutines.JobSupport.cancelImpl@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[15805]:0x3c4865
<CoilBug:composeApp>.kotlinx.coroutines.JobSupport.parentCancelled@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[15802]:0x3c47d7
<CoilBug:composeApp>.kotlinx.coroutines.ChildHandleNode.invoke@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[15867]:0x3c62c8
<CoilBug:composeApp>.kotlinx.coroutines.JobSupport.notifyCancelling@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[15773]:0x3c33df
<CoilBug:composeApp>.kotlinx.coroutines.JobSupport.tryMakeCancelling@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[15812]:0x3c4fed
<CoilBug:composeApp>.kotlinx.coroutines.JobSupport.makeCancelling@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[15810]:0x3c4e5b
<CoilBug:composeApp>.kotlinx.coroutines.JobSupport.cancelImpl@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[15805]:0x3c4865
<CoilBug:composeApp>.kotlinx.coroutines.JobSupport.parentCancelled@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[15802]:0x3c47d7
<CoilBug:composeApp>.kotlinx.coroutines.ChildHandleNode.invoke@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[15867]:0x3c62c8
<CoilBug:composeApp>.kotlinx.coroutines.JobSupport.notifyCancelling@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[15773]:0x3c33df
<CoilBug:composeApp>.kotlinx.coroutines.JobSupport.tryMakeCancelling@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[15812]:0x3c4fed
<CoilBug:composeApp>.kotlinx.coroutines.JobSupport.makeCancelling@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[15810]:0x3c4e5b
<CoilBug:composeApp>.kotlinx.coroutines.JobSupport.cancelImpl@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[15805]:0x3c4865
<CoilBug:composeApp>.kotlinx.coroutines.JobSupport.cancelInternal@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[15801]:0x3c47c1
<CoilBug:composeApp>.kotlinx.coroutines.JobSupport.cancel@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[15798]:0x3c46ed
<CoilBug:composeApp>.kotlinx.coroutines.Job.cancel$default@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[15629]:0x3c0e05
<CoilBug:composeApp>.coil3.compose.AsyncImagePainter.<set-rememberJob>@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[54479]:0x6f7de0
<CoilBug:composeApp>.coil3.compose.AsyncImagePainter.onForgotten@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[54509]:0x6f8232
<CoilBug:composeApp>.coil3.compose.internal.ContentPainterNode.onDetach@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[54661]:0x6fb8c2
<CoilBug:composeApp>.androidx.compose.ui.Node.runDetachLifecycle@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[33895]:0x53c2d1
<CoilBug:composeApp>.androidx.compose.ui.node.NodeChain.runDetachLifecycle@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[37874]:0x59b6a2
<CoilBug:composeApp>.androidx.compose.ui.node.LayoutNode.detach@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[37095]:0x587d6d
<CoilBug:composeApp>.androidx.compose.ui.node.LayoutNode.detach@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[37095]:0x587def
<CoilBug:composeApp>.androidx.compose.ui.node.LayoutNode.detach@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[37095]:0x587def
<CoilBug:composeApp>.androidx.compose.ui.node.LayoutNode.onChildRemoved@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[37088]:0x587382
<CoilBug:composeApp>.androidx.compose.ui.node.LayoutNode.removeAt@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[37086]:0x58728d
<CoilBug:composeApp>.androidx.compose.ui.platform.DefaultUiApplier.remove@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[40207]:0x5be628
<CoilBug:composeApp>.androidx.compose.runtime.changelist.RemoveNode.execute@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[23286]:0x4425a8
<CoilBug:composeApp>.androidx.compose.runtime.changelist.Operations.executeAndFlushAllPendingOperations@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[23451]:0x445e15
<CoilBug:composeApp>.androidx.compose.runtime.changelist.ChangeList.executeAndFlushAllPendingChanges@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[23043]:0x4384da
<CoilBug:composeApp>.androidx.compose.runtime.CompositionImpl.applyChangesInLocked@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[21704]:0x41440e
<CoilBug:composeApp>.androidx.compose.runtime.CompositionImpl.applyChanges@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[21705]:0x414a1d
<CoilBug:composeApp>.androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$slambda$lambda.invoke@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[22044]:0x41e59b
<CoilBug:composeApp>.androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$slambda$lambda.invoke@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[22045]:0x41ed2e
<CoilBug:composeApp>.androidx.compose.runtime.FrameAwaiter.resume@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[21102]:0x401dc4
<CoilBug:composeApp>.androidx.compose.runtime.BroadcastFrameClock.sendFrame@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[21120]:0x402476
<CoilBug:composeApp>.androidx.compose.ui.scene.BaseComposeScene.render@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[40650]:0x5c5b05
<CoilBug:composeApp>.androidx.compose.ui.window.<no name provided>.onRender@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[41315]:0x5d254f
<CoilBug:composeApp>.org.jetbrains.skiko.<no name provided>.drawFrame@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[28896]:0x4c4d98
<CoilBug:composeApp>.org.jetbrains.skiko.CanvasRenderer$needRedraw$lambda.invoke@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[28863]:0x4c4718
<CoilBug:composeApp>.org.jetbrains.skiko.CanvasRenderer$needRedraw$lambda.invoke@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[28864]:0x4c4771
<CoilBug:composeApp>.org.w3c.dom.__callFunction_((Double)->Unit)@http://localhost:8080/aa701647711f341d29ed.wasm:wasm-function[18326]:0x3f152f
org.w3c.dom.__convertKotlinClosureToJsClosure_((Double)->Unit)/<@webpack-internal:///./kotlin/composeApp.uninstantiated.mjs:1195:124
To Reproduce
Here is a very simple repository to reproduce: https://github.com/julius-b/coil-bug
It's basically just an AsyncImage
: https://github.com/julius-b/coil-bug/blob/main/composeApp/src/wasmJsMain/kotlin/ch/whatever/MainScreen.kt#L69
LaunchedEffect
that cancels the AsyncImage
: https://github.com/julius-b/coil-bug/blob/main/composeApp/src/wasmJsMain/kotlin/ch/whatever/App.kt#L30
The demo loads 20 images which increases the chance of this bug occurring to about every 2nd page reload.
note: In this sample, the bug can be prevented by changing whether the initial screen is even shown, which makes it so that the image loading is never cancelled.
NOTE: in this demo, the bug only occurs when the QuizView
(Screen
) also uses AsyncImage
, but in the project where I noticed this issue the QuizView
does not use AsyncImage
and the bug still occurs.
Also the bug is significantly rarer in Chromium (^ report was with Firefox) and the Stacktrace there is differnt:
Uncaught runtime errors:
×
ERROR
BodyStreamBuffer was aborted
AbortError: BodyStreamBuffer was aborted
at io.ktor.client.fetch.abort_$external_fun (webpack-internal:///./kotlin/composeApp.uninstantiated.mjs:4645:73)
at <CoilBug:composeApp>.io.ktor.client.engine.js.JsClientEngine$execute$lambda.invoke (http://localhost:8080/5bf60037f095d0dccd78.wasm:wasm-function[59948]:0x761a81)
at <CoilBug:composeApp>.io.ktor.client.engine.js.JsClientEngine$execute$lambda.invoke (http://localhost:8080/5bf60037f095d0dccd78.wasm:wasm-function[59949]:0x761aa9)
at <CoilBug:composeApp>.kotlinx.coroutines.InvokeOnCancelling.invoke (http://localhost:8080/5bf60037f095d0dccd78.wasm:wasm-function[15857]:0x3c6146)
at <CoilBug:composeApp>.kotlinx.coroutines.JobSupport.notifyCancelling (http://localhost:8080/5bf60037f095d0dccd78.wasm:wasm-function[15773]:0x3c33df)
at <CoilBug:composeApp>.kotlinx.coroutines.JobSupport.tryMakeCompletingSlowPath (http://localhost:8080/5bf60037f095d0dccd78.wasm:wasm-function[15816]:0x3c53e1)
at <CoilBug:composeApp>.kotlinx.coroutines.JobSupport.tryMakeCompleting (http://localhost:8080/5bf60037f095d0dccd78.wasm:wasm-function[15815]:0x3c5202)
at <CoilBug:composeApp>.kotlinx.coroutines.JobSupport.cancelMakeCompleting (http://localhost:8080/5bf60037f095d0dccd78.wasm:wasm-function[15806]:0x3c495c)
at <CoilBug:composeApp>.kotlinx.coroutines.JobSupport.cancelImpl (http://localhost:8080/5bf60037f095d0dccd78.wasm:wasm-function[15805]:0x3c4847)
at <CoilBug:composeApp>.kotlinx.coroutines.JobSupport.cancelInternal (http://localhost:8080/5bf60037f095d0dccd78.wasm:wasm-function[15801]:0x3c47c1)
(similar to #2462)
Version
coil = "3.1.0"
compose-multiplatform = "1.7.3"
kotlin = "2.1.10"
ktor = "3.0.3"