{"record":{"id":"0443b9bf67e60d91","repo":"google/ExoPlayer","slug":"multiple-ima-server-side-ad-insertion-sources-not","errorCode":null,"errorMessage":"Multiple IMA server side ad insertion sources not supported.","messagePattern":"Multiple IMA server side ad insertion sources not supported\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaServerSideAdInsertionMediaSource.java","lineNumber":1370,"sourceCode":"      AdOverlayInfo overlayInfo = adViewProvider.getAdOverlayInfos().get(i);\n      container.registerFriendlyObstruction(\n          imaSdkFactory.createFriendlyObstruction(\n              overlayInfo.view,\n              ImaUtil.getFriendlyObstructionPurpose(overlayInfo.purpose),\n              overlayInfo.reasonDetail != null ? overlayInfo.reasonDetail : \"Unknown reason\"));\n    }\n  }\n\n  private static void assertSingleInstanceInPlaylist(Player player) {\n    int counter = 0;\n    for (int i = 0; i < player.getMediaItemCount(); i++) {\n      MediaItem mediaItem = player.getMediaItemAt(i);\n      if (mediaItem.localConfiguration != null\n          && C.SSAI_SCHEME.equals(mediaItem.localConfiguration.uri.getScheme())\n          && ImaServerSideAdInsertionUriBuilder.IMA_AUTHORITY.equals(\n              mediaItem.localConfiguration.uri.getAuthority())) {\n        if (++counter > 1) {\n          throw new IllegalStateException(\n              \"Multiple IMA server side ad insertion sources not supported.\");\n        }\n      }\n    }\n  }\n\n  private class VodAdEventListener implements AdEventListener {\n    @Override\n    public void onAdEvent(AdEvent event) {\n      AdPlaybackState newAdPlaybackState = adPlaybackState;\n      switch (event.getType()) {\n        case CUEPOINTS_CHANGED:\n          if (newAdPlaybackState.equals(AdPlaybackState.NONE)) {\n            newAdPlaybackState =\n                setVodAdGroupPlaceholders(\n                    checkNotNull(streamManager).getCuePoints(), new AdPlaybackState(adsId));\n          }\n          break;","sourceCodeStart":1352,"sourceCodeEnd":1388,"githubUrl":"https://github.com/google/ExoPlayer/blob/dd430f7053a1a3958deea3ead6a0565150c06bfc/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaServerSideAdInsertionMediaSource.java#L1352-L1388","documentation":"assertSingleInstanceInPlaylist walks the player's playlist and counts items whose URI uses the imasky:// scheme with the IMA authority; if more than one is found it throws IllegalStateException. Server-side ad insertion through this extension is architected around a single active SSAI stream per player, so a second such item is rejected rather than playing incorrectly.","triggerScenarios":"Adding two or more MediaItems built with ImaServerSideAdInsertionUriBuilder (scheme imasky, IMA authority) to the same Player playlist — either directly via player.addMediaItem/setMediaItems, or by rebuilding/concatenating playlists that each contain an SSAI item.","commonSituations":"Refactoring a single-item player into a playlist and blindly concatenating SSAI URIs; restoring a saved playlist that contains the SSAI item while also inserting a fresh one; a media queue feature that lets users add a live DAI stream twice.","solutions":["Ensure exactly one IMA SSAI MediaItem exists per Player: filter the playlist before submission and drop or replace duplicates.","If multiple SSAI streams genuinely must play sequentially, create a new Player instance (release the old one) for each SSAI stream instead of putting both in one playlist.","If the duplicate is unintentional, audit the code path that constructs MediaItems (queue/restore logic) and de-duplicate on the imasky scheme."],"exampleFix":"// before\nplayer.setMediaItems(Arrays.asList(ssaiItem, ssaiItem)); // IllegalStateException\n\n// after\nList<MediaItem> items = new ArrayList<>();\nitems.add(ssaiItem);\nitems.add(MediaItem.fromUri(plainVodUri)); // at most one SSAI item per player\nplayer.setMediaItems(items);","handlingStrategy":"validation","validationCode":"static boolean hasMultipleSsaiItems(Player player) {\n  int count = 0;\n  for (int i = 0; i < player.getMediaItemCount(); i++) {\n    MediaItem mi = player.getMediaItemAt(i);\n    if (mi.localConfiguration != null\n        && C.SSAI_SCHEME.equals(mi.localConfiguration.uri.getScheme())) {\n      if (++count > 1) return true;\n    }\n  }\n  return false;\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["De-duplicate playlist entries on the imasky scheme before calling setMediaItems.","Create a new Player per SSAI stream when sequences are required."],"tags":["ima","ssai","playlist","validation"],"backgroundTag":null,"analyzedSha":"dd430f7053a1a3958deea3ead6a0565150c06bfc","analyzedAt":"2026-08-14T12:22:02.982Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}