{"record":{"id":"e76eebbc23be17cf","repo":"google/ExoPlayer","slug":"missing-implementation-to-handle-one-of-the-comman","errorCode":null,"errorMessage":"Missing implementation to handle one of the COMMAND_SEEK_*","messagePattern":"Missing implementation to handle one of the COMMAND_SEEK_\\*","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"library/common/src/main/java/com/google/android/exoplayer2/SimpleBasePlayer.java","lineNumber":3277,"sourceCode":"  /**\n   * Handles calls to {@link Player#seekTo} and other seek operations (for example, {@link\n   * Player#seekToNext}).\n   *\n   * <p>Will only be called if the appropriate {@link Player.Command}, for example {@link\n   * Player#COMMAND_SEEK_TO_MEDIA_ITEM} or {@link Player#COMMAND_SEEK_TO_NEXT}, is available.\n   *\n   * @param mediaItemIndex The media item index to seek to. The index is in the range 0 &lt;= {@code\n   *     mediaItemIndex} &lt; {@code mediaItems.size()}.\n   * @param positionMs The position in milliseconds to start playback from, or {@link C#TIME_UNSET}\n   *     to start at the default position in the media item.\n   * @param seekCommand The {@link Player.Command} used to trigger the seek.\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<?> handleSeek(\n      int mediaItemIndex, long positionMs, @Player.Command int seekCommand) {\n    throw new IllegalStateException(\"Missing implementation to handle one of the COMMAND_SEEK_*\");\n  }\n\n  @RequiresNonNull(\"state\")\n  private boolean shouldHandleCommand(@Player.Command int commandCode) {\n    return !released && state.availableCommands.contains(commandCode);\n  }\n\n  @SuppressWarnings(\"deprecation\") // Calling deprecated listener methods.\n  @RequiresNonNull(\"state\")\n  private void updateStateAndInformListeners(\n      State newState, boolean seeked, boolean isRepeatingCurrentItem) {\n    State previousState = state;\n    // Assign new state immediately such that all getters return the right values, but use a\n    // snapshot of the previous and new state so that listener invocations are triggered correctly.\n    this.state = newState;\n    if (newState.hasPositionDiscontinuity || newState.newlyRenderedFirstFrame) {\n      // Clear one-time events to avoid signalling them again later.\n      this.state =","sourceCodeStart":3259,"sourceCodeEnd":3295,"githubUrl":"https://github.com/google/ExoPlayer/blob/dd430f7053a1a3958deea3ead6a0565150c06bfc/library/common/src/main/java/com/google/android/exoplayer2/SimpleBasePlayer.java#L3259-L3295","documentation":"All seek operations (seekTo, seekToNext/Previous, seekBack/Forward, seekToDefaultPosition, etc.) are centralized in SimpleBasePlayer.handleSeek(int mediaItemIndex, long positionMs, @Player.Command int seekCommand). The default throws IllegalStateException when any seek command (e.g. COMMAND_SEEK_TO_MEDIA_ITEM, COMMAND_SEEK_TO_NEXT) is present in availableCommands without the override. The seekCommand parameter tells the handler which Player.Command triggered the seek.","triggerScenarios":"Calling any seek method on a custom SimpleBasePlayer whose State.availableCommands includes any of the COMMAND_SEEK_* constants while handleSeek is not overridden. Even advertising only COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM exposes it.","commonSituations":"A progress-bar seek in a player wrapper where availableCommands were copied wholesale from Player.Commands.DEFAULT (which includes many seek commands); implementing playback start/stop but treating seek as optional.","solutions":["Override handleSeek(int mediaItemIndex, long positionMs, @Player.Command int seekCommand): reposition the backing player, resolving C.TIME_UNSET to the default position, and return a future for the resulting state transition.","Or restrict availableCommands to the seek commands you actually support (often just COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM plus back/forward).","Map each advertised COMMAND_SEEK_* to correct behavior in tests — the base class picks this single handler for all of them."],"exampleFix":"// before\n// availableCommands includes COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM, no override:\n\n// after\n@Override\nprotected ListenableFuture<?> handleSeek(int mediaItemIndex, long positionMs, @Player.Command int seekCommand) {\n  long target = positionMs == C.TIME_UNSET ? 0 : positionMs;\n  return backendExecutor.submit(() -> engine.seekTo(mediaItemIndex, target));\n}","handlingStrategy":"validation","validationCode":"Player.Commands cmds = player.getState().availableCommands;\nboolean anySeek = cmds.contains(Player.COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM)\n    || cmds.contains(Player.COMMAND_SEEK_TO_NEXT)\n    || cmds.contains(Player.COMMAND_SEEK_TO_PREVIOUS)\n    || cmds.contains(Player.COMMAND_SEEK_TO_MEDIA_ITEM)\n    || cmds.contains(Player.COMMAND_SEEK_BACK)\n    || cmds.contains(Player.COMMAND_SEEK_FORWARD);\nif (anySeek) checkOverrides(playerClass, \"handleSeek\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["All COMMAND_SEEK_* constants share handleSeek — advertise only the ones you support.","Use the seekCommand parameter to distinguish programmatic seeks from next/previous/back/forward.","Resolve C.TIME_UNSET to the item's default position inside the handler."],"tags":["exoplayer","media3","player","seek","subclassing"],"backgroundTag":null,"analyzedSha":"dd430f7053a1a3958deea3ead6a0565150c06bfc","analyzedAt":"2026-08-14T12:22:02.982Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}