vico icon indicating copy to clipboard operation
vico copied to clipboard

Couple crashes with `java.util.ConcurrentModificationException` and `java.util.NoSuchElementException: Key null is missing in the map`

Open L-Andrade opened this issue 9 months ago • 10 comments

How to reproduce

Hey! Thanks again for the library. It's been working great so far, but we've started getting a couple crashes now after pushing it to more users (nothing major yet).

I've tried to reproduce it for the last couple days, but haven't managed to.

I have a LazyColumn with a CartesianChartHost. This is the basic gist of the chart:

ProvideVicoTheme(rememberM2VicoTheme()) {
    val modelProducer = remember { CartesianChartModelProducer.build() }
    LaunchedEffect(entries) {
        modelProducer.tryRunTransaction {
            lineSeries { series(x = entries.map { it.key }, y = entries.map { it.amount }) }
        }
    }
    // ...
    CartesianChartHost(
            modifier = modifier,
            scrollState = rememberVicoScrollState(scrollEnabled = false),
            modelProducer = modelProducer,
            diffAnimationSpec = spring(stiffness = Spring.StiffnessVeryLow),
            chart = rememberCartesianChart(
                rememberLineCartesianLayer(
                    lines = listOf(rememberLineSpec(shader = shader, backgroundShader = null)),
                    axisValueOverrider = remember {
                        object : AxisValueOverrider {
                            override fun getMinY(minY: Float, maxY: Float, extraStore: ExtraStore): Float {
                                return (minY - 0.0005f).coerceAtLeast(0f)
                            }

                            override fun getMaxY(minY: Float, maxY: Float, extraStore: ExtraStore): Float {
                                return (maxY + 0.0005f).coerceAtLeast(0.1f)
                            }
                        }
                    },
                ),
                decorations = listOf(
                    rememberHorizontalLine(
                        y = { state.start },
                        line = rememberLineComponent(
                            color = MaterialTheme.colors.gray80, shape = remember { DashedShape() },
                        ),
                    ),
                ),
                persistentMarkers = // Custom logic to display some markers
            ),
            marker = primaryMarker,
            markerVisibilityListener = // Custom logic for the marker visibility
        )
}

I have removed a few bits for brevity and because I can not share the whole code. Let me know if any of it is important to figuring this issue.

The chart is only shown if there are entries to display (entries is not empty). It seems to happen after the user scrolls to the bottom of the screen (chart is at the top).

Could it be because the CartesianChartModelProducer is recreated when the chart is visible again? Since it's in a LazyColumn, the chart leaves the composition and then returns once the user sees it again. Not sure it should matter though, since the chart is also a "new" one.

I'm not entirely sure if it is an issue on our end or an issue in Vico's end. Do you folks have any idea of how we can try to replicate this issue?

Let me know if you see any issue in our implementation too, please!

Observed behavior

There are a few crashes. I have a couple stack traces, not sure if they're linked to the same issue or not

      Fatal Exception: java.util.ConcurrentModificationException:
       at java.util.LinkedHashMap$LinkedHashIterator.nextNode(LinkedHashMap.java:760)
       at java.util.LinkedHashMap$LinkedValueIterator.next(LinkedHashMap.java:787)
       at kotlin.collections.CollectionsKt___CollectionsKt.mapTo(CollectionsKt___Collections.kt:1620)
       at com.patrykandpatrick.vico.core.cartesian.data.CartesianChartModelProducer.tryUpdate(CartesianChartModelProducer.kt:59)
       at com.patrykandpatrick.vico.core.cartesian.data.CartesianChartModelProducer.access$tryUpdate(CartesianChartModelProducer.kt:38)
       at com.patrykandpatrick.vico.core.cartesian.data.CartesianChartModelProducer$Transaction.tryCommit(CartesianChartModelProducer.kt:206)
       at com.patrykandpatrick.vico.core.cartesian.data.CartesianChartModelProducer.tryRunTransaction(CartesianChartModelProducer.kt:171)
       at com.getbux.android.stocks.ui.chart.ValueChartKt$ValueChart$1$1.invokeSuspend(ValueChart.kt:55)
       at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
       at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:104)
       at androidx.compose.ui.platform.AndroidUiDispatcher.performTrampolineDispatch(AndroidUiDispatcher.android.kt:81)
       at androidx.compose.ui.platform.AndroidUiDispatcher.access$performTrampolineDispatch(AndroidUiDispatcher.android.kt:41)
       at androidx.compose.ui.platform.AndroidUiDispatcher$dispatchCallback$1.run(AndroidUiDispatcher.android.kt:57)
       at android.os.Handler.handleCallback(Handler.java:958)
       at android.os.Handler.dispatchMessage(Handler.java:99)
       at android.os.Looper.loopOnce(Looper.java:224)
       at android.os.Looper.loop(Looper.java:318)
       at android.app.ActivityThread.main(ActivityThread.java:8677)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:561)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1013)

and

      Fatal Exception: java.util.NoSuchElementException: Key null is missing in the map.
       at kotlin.collections.MapsKt__MapWithDefaultKt.getOrImplicitDefaultNullable(MapsKt__MapWithDefault.kt:24)
       at kotlin.collections.MapsKt__MapsKt.getValue(MapsKt__Maps.kt:360)
       at com.patrykandpatrick.vico.core.cartesian.data.MutableChartValuesKt$toImmutable$1.getYRange(MutableChartValues.kt:122)
       at com.patrykandpatrick.vico.core.cartesian.layer.LineCartesianLayer.toDrawingModel(LineCartesianLayer.kt:598)
       at com.patrykandpatrick.vico.core.cartesian.layer.LineCartesianLayer.prepareForTransformation(LineCartesianLayer.kt:583)
       at com.patrykandpatrick.vico.core.cartesian.layer.LineCartesianLayer.prepareForTransformation(LineCartesianLayer.kt:73)
       at com.patrykandpatrick.vico.core.cartesian.CartesianChart$transformationPreparationModelAndLayerConsumer$1.invoke(CartesianChart.java:106)
       at com.patrykandpatrick.vico.core.cartesian.CartesianChart.consume(CartesianChart.kt:328)
       at com.patrykandpatrick.vico.core.cartesian.CartesianChart.forEachWithLayer(CartesianChart.kt:316)
       at com.patrykandpatrick.vico.core.cartesian.CartesianChart.prepareForTransformation(CartesianChart.kt:251)
       at com.patrykandpatrick.vico.compose.cartesian.CartesianChartModelProducerKt$collectAsState$2$1$2.invoke(CartesianChartModelProducer.kt:124)
       at com.patrykandpatrick.vico.compose.cartesian.CartesianChartModelProducerKt$collectAsState$2$1$2.invoke(CartesianChartModelProducer.kt:124)
       at com.patrykandpatrick.vico.core.cartesian.data.CartesianChartModelProducer$UpdateReceiver.handleUpdate(CartesianChartModelProducer.kt:229)
       at com.patrykandpatrick.vico.core.cartesian.data.CartesianChartModelProducer.registerForUpdates(CartesianChartModelProducer.kt:146)
       at com.patrykandpatrick.vico.compose.cartesian.CartesianChartModelProducerKt$collectAsState$2$1.invokeSuspend(CartesianChartModelProducer.kt:114)
       at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
       at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:104)
       at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.java:585)
       at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:802)
       at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:706)
       at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:693)

Expected behavior

The app should not crash

Vico version(s)

2.0.0-alpha.20

Android version(s)

12, 13, 14

Additional information

We're actually on 2.0.0-alpha.19, but there seem to be only small changes in alpha.20.

It's quite hard to figure out where the issue is coming from. Since the stack trace does not point to our app code at all, we don't know if it's an issue with our integration or an internal Vico issue.

And since we're not familiar with Vico's codebase, it's a bit harder to try to replicate it. I've tried scrolling up and down multiple times, adding delays in multiple places, adding more charts to the screen and scrolling, configuration changes, going to the background/foreground, spamming buttons that cause the entries to change, navigating back or to another screen, and more. Let me know if you have any ideas that could replicate it, any help would be great here!

Thanks in advance

L-Andrade avatar May 29 '24 11:05 L-Andrade