androidx / androidx/media

ArrayIndexOutOfBoundsException in PlaylistTimeline.getFirstWindowIndexByChildIndex during player release after pre-roll ad completes

Open
#3,142 10 comments 0 reactions 1 assignee View on GitHub

@icbaker is already working on this.

Since Mar 27, 2026.

question
Dominant language
Java
Stars
3k
Forks
955
Avg merge
12d 14h
Merged PRs (30d)
2

Description

When using MediaItem with AdsConfiguration and ImaAdsLoader registered via
DefaultMediaSourceFactory.setLocalAdInsertionComponents() inside a MediaLibraryService,
releasing the player after a pre-roll ad finishes and content begins playing causes a crash:

java.lang.ArrayIndexOutOfBoundsException: length=1; index=-1
at androidx.media3.exoplayer.PlaylistTimeline.getFirstWindowIndexByChildIndex(PlaylistTimeline.java:104)
at androidx.media3.exoplayer.AbstractConcatenatedTimeline.getPeriod(AbstractConcatenatedTimeline.java:230)
at androidx.media3.common.Timeline.getPeriod(Timeline.java:1332)

## Reproduction

The crash is state-dependent:

Scenario Result
No ad configured ✅ No crash
Ad currently playing when release() is called ✅ No crash
Ad failed / timed out, then release() called ❌ Crash
Ad finished, content playing, then release() called ❌ Crash
// ImaAdsLoader is created lazily and setPlayer() called inside the lambda                                                                                                                                                             
var imaAdsLoader: ImaAdsLoader? = null                                                                                                                                                                                                 
                                                                                                                                                                                                                                       
fun getClientSideAdsLoader(): AdsLoader? {                                                                                                                                                                                             
    if (imaAdsLoader == null) {                                                                                                                                                                                                        
        imaAdsLoader = ImaAdsLoader.Builder(context).build()
    }                                                                                                                                                                                                                                  
    imaAdsLoader?.setPlayer(player)
    return imaAdsLoader                                                                                                                                                                                                                
}                                                         

// Factory wires ImaAdsLoader via setLocalAdInsertionComponents                                                                                                                                                                        
val mediaSourceFactory = DefaultMediaSourceFactory(context)
    .setLocalAdInsertionComponents(                                                                                                                                                                                                    
        { _: MediaItem.AdsConfiguration? -> getClientSideAdsLoader() },
        playerView                                                                                                                                                                                                                     
    )                                                                                                                                                                                                                                  
 
// ExoPlayer built with the factory                                                                                                                                                                                                    
val player = ExoPlayer.Builder(context)                   
    .setMediaSourceFactory(mediaSourceFactory)
    .build()
                                                                                                                                                                                                                                       
// MediaItem carries the ad tag via AdsConfiguration
val mediaItem = MediaItem.Builder()                                                                                                                                                                                                    
    .setUri(contentUri)                                   
    .setAdsConfiguration(                                                                                                                                                                                                              
        MediaItem.AdsConfiguration.Builder(Uri.parse(adTagUrl)).build()
    )                                                                                                                                                                                                                                  
    .build()                                              
                                                                                                                                                                                                                                       
player.setMediaItem(mediaItem)
player.prepare()                                                                                                                                                                                                                       
player.play()                                             

// After pre-roll finishes and content is playing, press back:                                                                                                                                                                         
imaAdsLoader?.release()
player.release() // ← crash here                                                                                                                                                                                                       

Root Cause

During ExoPlayer.release(), the player walks the timeline to finalize state.
AbstractConcatenatedTimeline.getPeriod(periodIndex) calls
getChildIndexByPeriodIndex(periodIndex), which returns -1 when the period index
is unresolved — left over from the ads → content transition in the MediaPeriodQueue.
That -1 is then passed directly into PlaylistTimeline.getFirstWindowIndexByChildIndex(-1),
producing the ArrayIndexOutOfBoundsException.

This happens because after a pre-roll completes and ExoPlayer transitions to the content
period, the internal currentPeriodIndex can be left in an unresolved state. When
release() is called at that moment the timeline walk hits this invalid index.

Relation to #3125

This crash is not fixed by the patch in #3125 (included in 1.10.0-rc03). That commit
added bounds checks in AdsMediaSource.java and ExoPlayerImplInternal.java for
durationsUs and states arrays. The crash here originates in PlaylistTimeline.java
and AbstractConcatenatedTimeline.java, neither of which were modified by #3125.

Conclusion

We have been tracking this crash across multiple Media3 versions and it remains
reproducible on 1.10.0-rc03. We initially assumed #3125 would resolve it since the
stack trace looks similar, but after analyzing the commit diff it is clear the affected
classes are different.

Happy to provide a test additional logs if that would help move this forward.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.