Version
6.19.2 (also affects 6.19.1; same code on support/6.x.x)
What platforms are you having the problem on?
Android
System Version
Reproduced on Android 13 → Android 16. Most reports are Samsung devices but the bug is platform-wide; observed across 260+ unique users in production.
On what device are you experiencing the issue?
Real device (Samsung SM-S936B / Galaxy S24 among many others)
Architecture
Old architecture
What happened?
Fatal NullPointerException thrown from the main looper:
NullPointerException: Attempt to invoke interface method
'boolean androidx.media3.exoplayer.ExoPlayer.isPlayingAd()' on a null object reference
at com.brentvatne.exoplayer.ReactExoplayerView.videoLoaded(ReactExoplayerView.java:1486)
at com.brentvatne.exoplayer.ReactExoplayerView.onEvents(ReactExoplayerView.java:1443)
at androidx.media3.exoplayer.ExoPlayerImpl.lambda$new$0(ExoPlayerImpl.java:301)
at androidx.media3.common.util.ListenerSet$ListenerHolder.iterationFinished(ListenerSet.java:353)
at androidx.media3.common.util.ListenerSet.handleMessage(ListenerSet.java:297)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(...)
Root cause
videoLoaded() (line 1486 on support/6.x.x) calls player.isPlayingAd() directly on the field, with no null check:
// android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java
private void videoLoaded() {
if (!player.isPlayingAd() && loadVideoStarted) { // ← NPE here
...
}
}
This is a race condition between ListenerSet dispatch and the player being released:
- ExoPlayer's
ListenerSet posts a Player.Listener.onEvents callback on the main looper.
- Before the message is dispatched, the React view is unmounted →
cleanUpResources() / releasePlayer() runs and nulls player.
- The pending message fires →
onPlaybackStateChanged(STATE_READY) → videoLoaded() → player.isPlayingAd() on a null reference.
This happens in feed-style UIs (vertical/horizontal lists of videos remounted on scroll, or apps that key-remount <Video> on source change) where the unmount window overlaps with STATE_READY events.
Already-existing helper that should be used
A private null-safe wrapper already exists in the same file (support/6.x.x line 345):
private boolean isPlayingAd() {
return player != null && player.isPlayingAd();
}
…and is used elsewhere in the file (e.g. line 285). The call site at line 1486 was simply missed.
Suggested fix
Replace the raw field access at line 1486 with the existing helper:
private void videoLoaded() {
- if (!player.isPlayingAd() && loadVideoStarted) {
+ if (player == null) return;
+ if (!isPlayingAd() && loadVideoStarted) {
(The early player == null guard also protects the player.getVideoFormat() call a few lines below, which would NPE under the same race.)
Impact
In our production app: 392 crashes / 262 unique users over 6 months on a feed of looping background videos that key-remount on source change. Crash rate scales with how aggressively the app destroys/creates Video views.
Reproducer
Hard to nail down a deterministic repro because of the race, but it is easy to surface in a FlatList of <Video> items where the key prop changes on scroll (forces native release+create). The Sentry replays we have show the crash consistently happening right at the moment a video tile is swapped out.
Expected Behavior
videoLoaded() should be a no-op (or use the existing null-safe isPlayingAd() helper) when the player has already been released.
Version
6.19.2 (also affects 6.19.1; same code on
support/6.x.x)What platforms are you having the problem on?
Android
System Version
Reproduced on Android 13 → Android 16. Most reports are Samsung devices but the bug is platform-wide; observed across 260+ unique users in production.
On what device are you experiencing the issue?
Real device (Samsung SM-S936B / Galaxy S24 among many others)
Architecture
Old architecture
What happened?
Fatal
NullPointerExceptionthrown from the main looper:Root cause
videoLoaded()(line 1486 onsupport/6.x.x) callsplayer.isPlayingAd()directly on the field, with no null check:This is a race condition between
ListenerSetdispatch and the player being released:ListenerSetposts aPlayer.Listener.onEventscallback on the main looper.cleanUpResources()/releasePlayer()runs and nullsplayer.onPlaybackStateChanged(STATE_READY)→videoLoaded()→player.isPlayingAd()on a null reference.This happens in feed-style UIs (vertical/horizontal lists of videos remounted on scroll, or apps that key-remount
<Video>on source change) where the unmount window overlaps withSTATE_READYevents.Already-existing helper that should be used
A private null-safe wrapper already exists in the same file (
support/6.x.xline 345):…and is used elsewhere in the file (e.g. line 285). The call site at line 1486 was simply missed.
Suggested fix
Replace the raw field access at line 1486 with the existing helper:
private void videoLoaded() { - if (!player.isPlayingAd() && loadVideoStarted) { + if (player == null) return; + if (!isPlayingAd() && loadVideoStarted) {(The early
player == nullguard also protects theplayer.getVideoFormat()call a few lines below, which would NPE under the same race.)Impact
In our production app: 392 crashes / 262 unique users over 6 months on a feed of looping background videos that key-remount on source change. Crash rate scales with how aggressively the app destroys/creates Video views.
Reproducer
Hard to nail down a deterministic repro because of the race, but it is easy to surface in a FlatList of
<Video>items where thekeyprop changes on scroll (forces native release+create). The Sentry replays we have show the crash consistently happening right at the moment a video tile is swapped out.Expected Behavior
videoLoaded()should be a no-op (or use the existing null-safeisPlayingAd()helper) when the player has already been released.