Skip to content

Conversation

mozzius
Copy link
Member

@mozzius mozzius commented Sep 28, 2025

This was the cause of increased crash rates on Android.

Steps to reproduce the crash

  1. Make some toasts appear
  2. Scroll down a flatlist somewhat quickly at the moment they animate away
screen-20250928-134859.mp4

Crash logs

Note: this matches what we see in the Google Play console

 ERROR  Your app just crashed. See the error below.
java.lang.IllegalStateException: ReactViewGroup contains null child at index 1 when traversal in dispatchGetDisplayList, the view may have been removed.
  android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4524)
  android.view.View.updateDisplayListIfDirty(View.java:24084)
  android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4556)
  android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4529)
  android.view.View.updateDisplayListIfDirty(View.java:24084)
  android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4556)
  android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4529)
  android.view.View.updateDisplayListIfDirty(View.java:24084)
  android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4556)
  android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4529)
  android.view.View.updateDisplayListIfDirty(View.java:24084)
  android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4556)
  android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4529)
  android.view.View.updateDisplayListIfDirty(View.java:24084)
  android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4556)
  android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4529)
  android.view.View.updateDisplayListIfDirty(View.java:24084)
  android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4556)
  android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4529)
  android.view.View.updateDisplayListIfDirty(View.java:24084)
  android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4556)
  android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4529)
  android.view.View.updateDisplayListIfDirty(View.java:24084)
  android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4556)
  android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4529)
  android.view.View.updateDisplayListIfDirty(View.java:24084)
  android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4556)
  android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4529)
  android.view.View.updateDisplayListIfDirty(View.java:24084)
  android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4556)
  android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4529)
  android.view.View.updateDisplayListIfDirty(View.java:24084)
  android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4556)
  android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4529)
  android.view.View.updateDisplayListIfDirty(View.java:24084)
  android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:694)
  android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:700)
  android.view.ThreadedRenderer.draw(ThreadedRenderer.java:798)
  android.view.ViewRootImpl.draw(ViewRootImpl.java:5839)
  android.view.ViewRootImpl.performDraw(ViewRootImpl.java:5490)
  android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:4474)
  android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:3071)
  android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:10659)
  android.view.Choreographer$CallbackRecord.run(Choreographer.java:1570)
  android.view.Choreographer$CallbackRecord.run(Choreographer.java:1579)
  android.view.Choreographer.doCallbacks(Choreographer.java:1179)
  android.view.Choreographer.doFrame(Choreographer.java:1108)
  android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1553)
  android.os.Handler.handleCallback(Handler.java:1041)
  android.os.Handler.dispatchMessage(Handler.java:103)
  android.os.Looper.dispatchMessage(Looper.java:315)
  android.os.Looper.loopOnce(Looper.java:251)
  android.os.Looper.loop(Looper.java:349)
  android.app.ActivityThread.main(ActivityThread.java:9041)
  java.lang.reflect.Method.invoke(Native Method)
  com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:593)
  com.android.internal.os.ZygoteInit.main(ZygoteInit.java:929)

The quick fix

Remove Reanimated exiting layout animations from the toasts. Looks bad but better than a crash. This is what this PR is.

The slow fix

I'll make a minimal repro and file it with the Reanimated folks, see if they can help. I had a look at the sonner-native code and it looks pretty standard, they're not doing anything wrong as far as I can see - that said, our previous toast code also used reanimated layout animations and didn't suffer from this, so maybe there is an easy fix that I'm not seeing.

Copy link

Old size New size Diff
9.41 MB 9.41 MB 0 B (0.00%)

@mozzius
Copy link
Member Author

mozzius commented Sep 29, 2025

Tried and failed to make a minimal repro :/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants