{"record":{"id":"94ef75062aa5021d","repo":"google/ExoPlayer","slug":"missing-implementation-to-handle-command-set-media","errorCode":null,"errorMessage":"Missing implementation to handle COMMAND_SET_MEDIA_ITEM(S)","messagePattern":"Missing implementation to handle COMMAND_SET_MEDIA_ITEM\\(S\\)","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"library/common/src/main/java/com/google/android/exoplayer2/SimpleBasePlayer.java","lineNumber":3182,"sourceCode":"  /**\n   * Handles calls to {@link Player#setMediaItem} and {@link Player#setMediaItems}.\n   *\n   * <p>Will only be called if {@link Player#COMMAND_SET_MEDIA_ITEM} or {@link\n   * Player#COMMAND_CHANGE_MEDIA_ITEMS} is available. If only {@link Player#COMMAND_SET_MEDIA_ITEM}\n   * is available, the list of media items will always contain exactly one item.\n   *\n   * @param mediaItems The media items to add.\n   * @param startIndex The index at which to start playback from, or {@link C#INDEX_UNSET} to start\n   *     at the default item.\n   * @param startPositionMs The position in milliseconds to start playback from, or {@link\n   *     C#TIME_UNSET} to start at the default position in the media item.\n   * @return A {@link ListenableFuture} indicating the completion of all immediate {@link State}\n   *     changes caused by this call.\n   */\n  @ForOverride\n  protected ListenableFuture<?> handleSetMediaItems(\n      List<MediaItem> mediaItems, int startIndex, long startPositionMs) {\n    throw new IllegalStateException(\"Missing implementation to handle COMMAND_SET_MEDIA_ITEM(S)\");\n  }\n\n  /**\n   * Handles calls to {@link Player#addMediaItem} and {@link Player#addMediaItems}.\n   *\n   * <p>Will only be called if {@link Player#COMMAND_CHANGE_MEDIA_ITEMS} is available.\n   *\n   * @param index The index at which to add the items. The index is in the range 0 &lt;= {@code\n   *     index} &lt;= {@link #getMediaItemCount()}.\n   * @param mediaItems The media items to add.\n   * @return A {@link ListenableFuture} indicating the completion of all immediate {@link State}\n   *     changes caused by this call.\n   */\n  @ForOverride\n  protected ListenableFuture<?> handleAddMediaItems(int index, List<MediaItem> mediaItems) {\n    throw new IllegalStateException(\"Missing implementation to handle COMMAND_CHANGE_MEDIA_ITEMS\");\n  }\n","sourceCodeStart":3164,"sourceCodeEnd":3200,"githubUrl":"https://github.com/google/ExoPlayer/blob/dd430f7053a1a3958deea3ead6a0565150c06bfc/library/common/src/main/java/com/google/android/exoplayer2/SimpleBasePlayer.java#L3164-L3200","documentation":"Player.setMediaItem/setMediaItems are dispatched by SimpleBasePlayer to handleSetMediaItems(List, int startIndex, long startPositionMs). The default throws IllegalStateException (note the '(S)' in the message covering both single and plural APIs) when COMMAND_SET_MEDIA_ITEM or COMMAND_CHANGE_MEDIA_ITEMS is advertised without the override. If only COMMAND_SET_MEDIA_ITEM is available, the list will contain exactly one item.","triggerScenarios":"Calling player.setMediaItem(item) or setMediaItems(list) on a custom SimpleBasePlayer whose State.availableCommands includes COMMAND_SET_MEDIA_ITEM / COMMAND_CHANGE_MEDIA_ITEMS while handleSetMediaItems is not overridden.","commonSituations":"Any custom SimpleBasePlayer expected to play content — playlist handling is the first thing implemented, but often the command set is widened before the handler is finished; wrapping MediaController-like sessions where the set commands are forwarded from a remote side.","solutions":["Override handleSetMediaItems(List<MediaItem> mediaItems, int startIndex, long startPositionMs): replace the playlist, resolve C.INDEX_UNSET / C.TIME_UNSET defaults, and return a future for the resulting State changes.","If playlist replacement is unsupported, remove COMMAND_SET_MEDIA_ITEM and COMMAND_CHANGE_MEDIA_ITEMS from availableCommands.","Add an integration test that calls setMediaItem immediately after construction — this error fires on the first such call."],"exampleFix":"// before\n// availableCommands includes COMMAND_SET_MEDIA_ITEM, no override:\n\n// after\n@Override\nprotected ListenableFuture<?> handleSetMediaItems(List<MediaItem> mediaItems, int startIndex, long startPositionMs) {\n  state = new State.Builder()\n      .setPlaylist(mediaItems)\n      .setCurrentMediaItemIndex(startIndex == C.INDEX_UNSET ? 0 : startIndex)\n      .setContentPositionMs(startPositionMs == C.TIME_UNSET ? 0 : startPositionMs)\n      .build();\n  invalidateState();\n  return Futures.immediateVoidFuture();\n}","handlingStrategy":"validation","validationCode":"boolean canSet = player.isCommandAvailable(Player.COMMAND_SET_MEDIA_ITEM)\n    || player.isCommandAvailable(Player.COMMAND_CHANGE_MEDIA_ITEMS);\nif (canSet) checkOverrides(playerClass, \"handleSetMediaItems\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Handle C.INDEX_UNSET and C.TIME_UNSET defaults in startIndex/startPositionMs.","With only COMMAND_SET_MEDIA_ITEM the list has exactly one item — do not assume arbitrary lists.","Call invalidateState() (or return work via the future) so listeners see the new playlist."],"tags":["exoplayer","media3","player","playlist","subclassing"],"backgroundTag":null,"analyzedSha":"dd430f7053a1a3958deea3ead6a0565150c06bfc","analyzedAt":"2026-08-14T12:22:02.982Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}