google/ExoPlayer · error · IllegalStateException

Missing implementation to handle one of the COMMAND_SEEK_*

Error message

Missing implementation to handle one of the COMMAND_SEEK_*

What it means

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.

Source

Thrown at library/common/src/main/java/com/google/android/exoplayer2/SimpleBasePlayer.java:3277

  /**
   * Handles calls to {@link Player#seekTo} and other seek operations (for example, {@link
   * Player#seekToNext}).
   *
   * <p>Will only be called if the appropriate {@link Player.Command}, for example {@link
   * Player#COMMAND_SEEK_TO_MEDIA_ITEM} or {@link Player#COMMAND_SEEK_TO_NEXT}, is available.
   *
   * @param mediaItemIndex The media item index to seek to. The index is in the range 0 &lt;= {@code
   *     mediaItemIndex} &lt; {@code mediaItems.size()}.
   * @param positionMs The position in milliseconds to start playback from, or {@link C#TIME_UNSET}
   *     to start at the default position in the media item.
   * @param seekCommand The {@link Player.Command} used to trigger the seek.
   * @return A {@link ListenableFuture} indicating the completion of all immediate {@link State}
   *     changes caused by this call.
   */
  @ForOverride
  protected ListenableFuture<?> handleSeek(
      int mediaItemIndex, long positionMs, @Player.Command int seekCommand) {
    throw new IllegalStateException("Missing implementation to handle one of the COMMAND_SEEK_*");
  }

  @RequiresNonNull("state")
  private boolean shouldHandleCommand(@Player.Command int commandCode) {
    return !released && state.availableCommands.contains(commandCode);
  }

  @SuppressWarnings("deprecation") // Calling deprecated listener methods.
  @RequiresNonNull("state")
  private void updateStateAndInformListeners(
      State newState, boolean seeked, boolean isRepeatingCurrentItem) {
    State previousState = state;
    // Assign new state immediately such that all getters return the right values, but use a
    // snapshot of the previous and new state so that listener invocations are triggered correctly.
    this.state = newState;
    if (newState.hasPositionDiscontinuity || newState.newlyRenderedFirstFrame) {
      // Clear one-time events to avoid signalling them again later.
      this.state =

View on GitHub (pinned to dd430f7053)

Solutions

  1. 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.
  2. Or restrict availableCommands to the seek commands you actually support (often just COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM plus back/forward).
  3. Map each advertised COMMAND_SEEK_* to correct behavior in tests — the base class picks this single handler for all of them.

Example fix

// before
// availableCommands includes COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM, no override:

// after
@Override
protected ListenableFuture<?> handleSeek(int mediaItemIndex, long positionMs, @Player.Command int seekCommand) {
  long target = positionMs == C.TIME_UNSET ? 0 : positionMs;
  return backendExecutor.submit(() -> engine.seekTo(mediaItemIndex, target));
}
Defensive patterns

Strategy: validation

Validate before calling

Player.Commands cmds = player.getState().availableCommands;
boolean anySeek = cmds.contains(Player.COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM)
    || cmds.contains(Player.COMMAND_SEEK_TO_NEXT)
    || cmds.contains(Player.COMMAND_SEEK_TO_PREVIOUS)
    || cmds.contains(Player.COMMAND_SEEK_TO_MEDIA_ITEM)
    || cmds.contains(Player.COMMAND_SEEK_BACK)
    || cmds.contains(Player.COMMAND_SEEK_FORWARD);
if (anySeek) checkOverrides(playerClass, "handleSeek");

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of google/ExoPlayer@dd430f7053 (2026-08-14). Data as JSON: /api/errors/e76eebbc23be17cf. Report an issue: GitHub.