braze-android-sdk icon indicating copy to clipboard operation
braze-android-sdk copied to clipboard

[Bug]: Crash IllegalArgumentException in compose ContentCardsList

Open artyomtarassov opened this issue 9 months ago • 5 comments

Braze Android SDK Version

30.4.0

Steps To Reproduce

Not able to reproduce, saw the crashes from Crashlytics

Expected Behavior

No crash

Actual Incorrect Behavior

Crash with IllegalArgumentException, stack trace attached

Fatal Exception: java.lang.IllegalArgumentException: Key "NjU0ZTBiNDRkZjc0YjkwMDRlMGQ3MDc0XyRfY2M9MzJkZTRlN2ItNmFlYy1jYThlLWNiOWQtYjljOGU1YzYyYjEzJmRpJmRtJm12PTY1NGUwYjQ0ZGY3NGI5MDA0ZTBkNzA3NiZvZCZwaT13ZnMmdz02NTRlMGI0NGRmNzRiOTAwNGUwZDcwMmYmd3A9MTcxNDE0NjkzMSZ3dj02NTRlMGI0NGRmNzRiOTAwNGUwZDczNmM=" was already used. If you are using LazyColumn/Row please make sure you provide a unique key for each item.
       at androidx.compose.ui.layout.LayoutNodeSubcompositionsState.subcompose(LayoutNodeSubcompositionsState.java:437)
       at androidx.compose.ui.layout.LayoutNodeSubcompositionsState$Scope.subcompose(SubcomposeLayout.kt:872)
       at androidx.compose.foundation.lazy.layout.LazyLayoutMeasureScopeImpl.measure-0kLqBqw(LazyLayoutMeasureScope.kt:125)
       at androidx.compose.foundation.lazy.LazyListMeasuredItemProvider.getAndMeasure(LazyListMeasuredItemProvider.java:48)
       at androidx.compose.foundation.lazy.LazyListMeasureKt.measureLazyList-5IMabDg(LazyListMeasure.kt:195)
       at androidx.compose.foundation.lazy.LazyListKt$rememberLazyListMeasurePolicy$1$1.invoke-0kLqBqw(LazyList.kt:313)
       at androidx.compose.foundation.lazy.LazyListKt$rememberLazyListMeasurePolicy$1$1.invoke(LazyList.kt:178)
       at androidx.compose.foundation.lazy.layout.LazyLayoutKt$LazyLayout$3$2$1.invoke-0kLqBqw(LazyLayout.kt:107)
       at androidx.compose.foundation.lazy.layout.LazyLayoutKt$LazyLayout$3$2$1.invoke(LazyLayout.kt:100)
       at androidx.compose.ui.layout.LayoutNodeSubcompositionsState$createMeasurePolicy$1.measure-3p2s80s(SubcomposeLayout.kt:709)
       at androidx.compose.ui.node.InnerNodeCoordinator.measure-BRTryo0(InnerNodeCoordinator.kt:126)
       at androidx.compose.foundation.AndroidOverscroll_androidKt$StretchOverscrollNonClippingLayer$2.invoke-3p2s80s(AndroidOverscroll_android.kt:584)
       at androidx.compose.foundation.AndroidOverscroll_androidKt$StretchOverscrollNonClippingLayer$2.invoke(AndroidOverscroll_android.kt:583)
       at androidx.compose.ui.layout.LayoutModifierImpl.measure-3p2s80s(LayoutModifier.kt:294)
       at androidx.compose.ui.node.LayoutModifierNodeCoordinator.measure-BRTryo0(LayoutModifierNodeCoordinator.kt:116)
       at androidx.compose.foundation.AndroidOverscroll_androidKt$StretchOverscrollNonClippingLayer$1.invoke-3p2s80s(AndroidOverscroll.android.kt:568)
       at androidx.compose.foundation.AndroidOverscroll_androidKt$StretchOverscrollNonClippingLayer$1.invoke(AndroidOverscroll.android.kt:567)
       at androidx.compose.ui.layout.LayoutModifierImpl.measure-3p2s80s(LayoutModifier.kt:294)
       at androidx.compose.ui.node.LayoutModifierNodeCoordinator.measure-BRTryo0(LayoutModifierNodeCoordinator.kt:116)
       at androidx.compose.ui.graphics.SimpleGraphicsLayerModifier.measure-3p2s80s(GraphicsLayerModifier.kt:646)
       at androidx.compose.ui.node.LayoutModifierNodeCoordinator.measure-BRTryo0(LayoutModifierNodeCoordinator.kt:116)
       at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasureBlock$1.invoke(LayoutNodeLayoutDelegate.java:252)
       at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasureBlock$1.invoke(LayoutNodeLayoutDelegate.java:251)
       at androidx.compose.runtime.snapshots.Snapshot$Companion.observe(Snapshot.java:2303)
       at androidx.compose.runtime.snapshots.SnapshotStateObserver$ObservedScopeMap.observe(SnapshotStateObserver.kt:500)
       at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver.kt:256)
       at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui_release(OwnerSnapshotObserver.kt:133)
       at androidx.compose.ui.node.OwnerSnapshotObserver.observeMeasureSnapshotReads$ui_release(OwnerSnapshotObserver.kt:113)
       at androidx.compose.ui.node.LayoutNodeLayoutDelegate.performMeasure-BRTryo0(LayoutNodeLayoutDelegate.java:1617)
       at androidx.compose.ui.node.LayoutNodeLayoutDelegate.access$performMeasure-BRTryo0(LayoutNodeLayoutDelegate.java:36)
       at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate.remeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:620)
       at androidx.compose.ui.node.LayoutNode.remeasure-_Sx5XlM$ui_release(LayoutNode.java:1145)
       at androidx.compose.ui.node.LayoutNode.remeasure-_Sx5XlM$ui_release$default(LayoutNode.java:1136)
       at androidx.compose.ui.node.MeasureAndLayoutDelegate.doRemeasure-sdFAvZA(MeasureAndLayoutDelegate.kt:356)
       at androidx.compose.ui.node.MeasureAndLayoutDelegate.remeasureAndRelayoutIfNeeded(MeasureAndLayoutDelegate.kt:514)
       at androidx.compose.ui.node.MeasureAndLayoutDelegate.remeasureAndRelayoutIfNeeded$default(MeasureAndLayoutDelegate.kt:491)
       at androidx.compose.ui.node.MeasureAndLayoutDelegate.measureAndLayout(MeasureAndLayoutDelegate.kt:377)
       at androidx.compose.ui.platform.AndroidComposeView.measureAndLayout(AndroidComposeView.android.kt:971)
       at androidx.compose.ui.node.Owner.measureAndLayout$default(Owner.java:228)
       at androidx.compose.ui.platform.AndroidComposeView.dispatchDraw(AndroidComposeView.android.kt:1224)
       at android.view.View.draw(View.java:23303)
       at android.view.View.updateDisplayListIfDirty(View.java:22151)
       at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4593)
       at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4566)
       at android.view.View.updateDisplayListIfDirty(View.java:22097)
       at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4593)
       at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4566)
       at android.view.View.updateDisplayListIfDirty(View.java:22097)
       at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4593)
       at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4566)
       at android.view.View.updateDisplayListIfDirty(View.java:22097)
       at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4593)
       at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4566)
       at android.view.View.updateDisplayListIfDirty(View.java:22097)
       at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4593)
       at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4566)
       at android.view.View.updateDisplayListIfDirty(View.java:22097)
       at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4593)
       at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4566)
       at android.view.View.updateDisplayListIfDirty(View.java:22097)
       at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4593)
       at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4566)
       at android.view.View.updateDisplayListIfDirty(View.java:22097)
       at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4593)
       at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4566)
       at android.view.View.updateDisplayListIfDirty(View.java:22097)
       at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4593)
       at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4566)
       at android.view.View.updateDisplayListIfDirty(View.java:22097)
       at android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:682)
       at android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:688)
       at android.view.ThreadedRenderer.draw(ThreadedRenderer.java:786)
       at android.view.ViewRootImpl.draw(ViewRootImpl.java:4678)
       at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:4389)
       at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:3604)
       at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:2372)
       at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:9082)
       at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1234)
       at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1242)
       at android.view.Choreographer.doCallbacks(Choreographer.java:902)
       at android.view.Choreographer.doFrame(Choreographer.java:835)
       at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1217)
       at android.os.Handler.handleCallback(Handler.java:942)
       at android.os.Handler.dispatchMessage(Handler.java:99)
       at android.os.Looper.loopOnce(Looper.java:201)
       at android.os.Looper.loop(Looper.java:288)
       at android.app.ActivityThread.main(ActivityThread.java:8066)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:703)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:911)

Verbose Logs

No response

Additional Information

No response

artyomtarassov avatar May 06 '24 15:05 artyomtarassov

Can you let us know what parameters, if any, you're passing to ContentCardList()?

bryanlogan avatar May 06 '24 15:05 bryanlogan

We pass the parameters emptyComposable, style, cardStyle, cardUpdateHandler, onCardClicked and customCardComposer

artyomtarassov avatar May 06 '24 15:05 artyomtarassov

Can you send what you're passing for cardUpdateHandler? If you don't want to post here, you can send it to Braze support.

bryanlogan avatar May 06 '24 15:05 bryanlogan

Yes, sure

    fun cardUpdateHandler(cards: List<Card>): List<Card> {
        val cardComparator = Comparator { cardA: Card, cardB: Card ->
            when {
                // A displays above B since A is pinned and B isn't
                cardA.isPinned && !cardB.isPinned -> -1
                // B displays above A since B is pinned and A isn't
                !cardA.isPinned && cardB.isPinned -> 1
                // At this point, both A & B are pinned or both A & B are non-pinned
                // A displays above B if A is newer
                cardA.created > cardB.created -> -1
                // B displays above A if B is newer
                cardA.created < cardB.created -> 1
                // They're considered equal at this point
                else -> 0
            }
        }
        return cards.sortedWith(cardComparator)
            .onEach { card ->
                card.isDismissibleByUser = false
            }
    }

artyomtarassov avatar May 07 '24 05:05 artyomtarassov

OK. I'm not able to recreate it either, but I'm adding a fix that will prevent duplicate ID's, which shouldn't be occurring.

As a side note, you may want to update your cardUpdateHandler to filter cards that have containsInvalidBrazeAction() just to match the default implementation.

bryanlogan avatar May 08 '24 15:05 bryanlogan

@bryanlogan when are you planning to do the fix, We have the same crash reported as well

mozarty avatar Jun 03 '24 09:06 mozarty

Release 32.0.0 fixes this issue if duplicate cards are passed in.

bryanlogan avatar Jul 29 '24 19:07 bryanlogan

Issue still happens after upgrading the sdk to 32.0.0

  Fatal Exception: java.lang.IllegalArgumentException: Key "NjZhOTA3Y2IxMDdkZmYxYzI0YTFlOTQwXyRfY2M9MTMwMGE3NGYtYTM0ZC0zOTRkLTlkZTktZjQwZGY3ZDdhMGQzJmRpJmRtJm12PTY2YTkwOGVkZmRjZjEwMDA1YzZjNmE2YyZvZCZwaT13ZnMmdz02NjM4OWM3YjJmNmFkZTAwNTk3NTc4ZGEmd3A9MTcyNTAwNTIyOSZ3dj02NjM4OWM3YzJmNmFkZTAwNTk3NTdhMDA=" was already used. If you are using LazyColumn/Row please make sure you provide a unique key for each item.
       at androidx.compose.ui.layout.LayoutNodeSubcompositionsState.subcompose(LayoutNodeSubcompositionsState.java:437)
       at androidx.compose.ui.layout.LayoutNodeSubcompositionsState$Scope.subcompose(SubcomposeLayout.kt:872)
       at androidx.compose.foundation.lazy.layout.LazyLayoutMeasureScopeImpl.measure-0kLqBqw(LazyLayoutMeasureScope.kt:125)
       at androidx.compose.foundation.lazy.LazyListMeasuredItemProvider.getAndMeasure(LazyListMeasuredItemProvider.java:48)
       at androidx.compose.foundation.lazy.LazyListMeasureKt.measureLazyList-5IMabDg(LazyListMeasure.kt:195)
       at androidx.compose.foundation.lazy.LazyListKt$rememberLazyListMeasurePolicy$1$1.invoke-0kLqBqw(LazyList.kt:313)
       at androidx.compose.foundation.lazy.LazyListKt$rememberLazyListMeasurePolicy$1$1.invoke(LazyList.kt:178)
       at androidx.compose.foundation.lazy.layout.LazyLayoutKt$LazyLayout$3$2$1.invoke-0kLqBqw(LazyLayout.kt:107)
       at androidx.compose.foundation.lazy.layout.LazyLayoutKt$LazyLayout$3$2$1.invoke(LazyLayout.kt:100)
       at androidx.compose.ui.layout.LayoutNodeSubcompositionsState$createMeasurePolicy$1.measure-3p2s80s(SubcomposeLayout.kt:709)
       at androidx.compose.ui.node.InnerNodeCoordinator.measure-BRTryo0(InnerNodeCoordinator.kt:126)
       at androidx.compose.foundation.AndroidOverscroll_androidKt$StretchOverscrollNonClippingLayer$2.invoke-3p2s80s(AndroidOverscroll_android.kt:584)
       at androidx.compose.foundation.AndroidOverscroll_androidKt$StretchOverscrollNonClippingLayer$2.invoke(AndroidOverscroll_android.kt:583)
       at androidx.compose.ui.layout.LayoutModifierImpl.measure-3p2s80s(LayoutModifier.kt:294)
       at androidx.compose.ui.node.LayoutModifierNodeCoordinator.measure-BRTryo0(LayoutModifierNodeCoordinator.kt:116)
       at androidx.compose.foundation.AndroidOverscroll_androidKt$StretchOverscrollNonClippingLayer$1.invoke-3p2s80s(AndroidOverscroll.android.kt:568)
       at androidx.compose.foundation.AndroidOverscroll_androidKt$StretchOverscrollNonClippingLayer$1.invoke(AndroidOverscroll.android.kt:567)
       at androidx.compose.ui.layout.LayoutModifierImpl.measure-3p2s80s(LayoutModifier.kt:294)
       at androidx.compose.ui.node.LayoutModifierNodeCoordinator.measure-BRTryo0(LayoutModifierNodeCoordinator.kt:116)
       at androidx.compose.ui.graphics.SimpleGraphicsLayerModifier.measure-3p2s80s(GraphicsLayerModifier.kt)
       at androidx.compose.ui.node.LayoutModifierNodeCoordinator.measure-BRTryo0(LayoutModifierNodeCoordinator.kt:116)
       at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasureBlock$1.invoke(LayoutNodeLayoutDelegate.java:252)
       at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasureBlock$1.invoke(LayoutNodeLayoutDelegate.java:251)
       at androidx.compose.runtime.snapshots.Snapshot$Companion.observe(Snapshot.java:2303)
       at androidx.compose.runtime.snapshots.SnapshotStateObserver$ObservedScopeMap.observe(SnapshotStateObserver.kt:500)
       at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver.kt:256)
       at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui_release(OwnerSnapshotObserver.kt:133)
       at androidx.compose.ui.node.OwnerSnapshotObserver.observeMeasureSnapshotReads$ui_release(OwnerSnapshotObserver.kt:113)
       at androidx.compose.ui.node.LayoutNodeLayoutDelegate.performMeasure-BRTryo0(LayoutNodeLayoutDelegate.java:1617)
       at androidx.compose.ui.node.LayoutNodeLayoutDelegate.access$performMeasure-BRTryo0(LayoutNodeLayoutDelegate.java:36)
       at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate.remeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:620)
       at androidx.compose.ui.node.LayoutNode.remeasure-_Sx5XlM$ui_release(LayoutNode.java:1145)
       at androidx.compose.ui.node.LayoutNode.remeasure-_Sx5XlM$ui_release$default(LayoutNode.java:1136)
       at androidx.compose.ui.node.MeasureAndLayoutDelegate.doRemeasure-sdFAvZA(MeasureAndLayoutDelegate.kt:356)
       at androidx.compose.ui.node.MeasureAndLayoutDelegate.remeasureAndRelayoutIfNeeded(MeasureAndLayoutDelegate.kt:514)
       at androidx.compose.ui.node.MeasureAndLayoutDelegate.remeasureAndRelayoutIfNeeded$default(MeasureAndLayoutDelegate.kt:491)
       at androidx.compose.ui.node.MeasureAndLayoutDelegate.measureAndLayout(MeasureAndLayoutDelegate.kt:377)
       at androidx.compose.ui.platform.AndroidComposeView.measureAndLayout(AndroidComposeView.android.kt:971)
       at androidx.compose.ui.node.Owner.measureAndLayout$default(Owner.java:228)
       at androidx.compose.ui.platform.AndroidComposeView.dispatchDraw(AndroidComposeView.android.kt:1224)
       at android.view.View.draw(View.java:23303)
       at android.view.View.updateDisplayListIfDirty(View.java:22151)
       at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4592)
       at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4565)
       at android.view.View.updateDisplayListIfDirty(View.java:22097)
       at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4592)
       at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4565)
       at android.view.View.updateDisplayListIfDirty(View.java:22097)
       at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4592)
       at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4565)
       at android.view.View.updateDisplayListIfDirty(View.java:22097)
       at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4592)
       at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4565)
       at android.view.View.updateDisplayListIfDirty(View.java:22097)
       at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4592)
       at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4565)
       at android.view.View.updateDisplayListIfDirty(View.java:22097)
       at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4592)
       at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4565)
       at android.view.View.updateDisplayListIfDirty(View.java:22097)
       at android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:682)
       at android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:688)
       at android.view.ThreadedRenderer.draw(ThreadedRenderer.java:786)
       at android.view.ViewRootImpl.draw(ViewRootImpl.java:4685)
       at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:4396)
       at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:3611)
       at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:2380)
       at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:9150)
       at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1234)
       at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1242)
       at android.view.Choreographer.doCallbacks(Choreographer.java:902)
       at android.view.Choreographer.doFrame(Choreographer.java:835)
       at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1217)
       at android.os.Handler.handleCallback(Handler.java:942)
       at android.os.Handler.dispatchMessage(Handler.java:99)
       at android.os.Looper.loopOnce(Looper.java:201)
       at android.os.Looper.loop(Looper.java:288)
       at android.app.ActivityThread.main(ActivityThread.java:8061)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:703)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:923)

mozarty avatar Aug 30 '24 10:08 mozarty

@mozarty Are you able to recreate this? We haven't been able to get clear recreation steps. In your case, are you seeing Card ID ___ already exists. Skipping card in the logcat?

bryanlogan avatar Aug 30 '24 13:08 bryanlogan

I can only see the report on crashlytics, I haven't seen it in my local sessions

mozarty avatar Aug 30 '24 16:08 mozarty