google/ExoPlayer · error · IllegalStateException

Missing implementation to handle COMMAND_PLAY_PAUSE

Error message

Missing implementation to handle COMMAND_PLAY_PAUSE

What it means

SimpleBasePlayer is an abstract base that routes Player commands to protected handle* methods; the default implementations throw IllegalStateException. This one fires when the player state advertises COMMAND_PLAY_PAUSE as available but the subclass did not override handleSetPlayWhenReady(boolean). It is a contract violation inside a custom player implementation, not a runtime media error.

Source

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

    return new MediaItemData.Builder(new PlaceholderUid())
        .setMediaItem(mediaItem)
        .setIsDynamic(true)
        .setIsPlaceholder(true)
        .build();
  }

  /**
   * Handles calls to {@link Player#setPlayWhenReady}, {@link Player#play} and {@link Player#pause}.
   *
   * <p>Will only be called if {@link Player#COMMAND_PLAY_PAUSE} is available.
   *
   * @param playWhenReady The requested {@link State#playWhenReady}
   * @return A {@link ListenableFuture} indicating the completion of all immediate {@link State}
   *     changes caused by this call.
   */
  @ForOverride
  protected ListenableFuture<?> handleSetPlayWhenReady(boolean playWhenReady) {
    throw new IllegalStateException("Missing implementation to handle COMMAND_PLAY_PAUSE");
  }

  /**
   * Handles calls to {@link Player#prepare}.
   *
   * <p>Will only be called if {@link Player#COMMAND_PREPARE} is available.
   *
   * @return A {@link ListenableFuture} indicating the completion of all immediate {@link State}
   *     changes caused by this call.
   */
  @ForOverride
  protected ListenableFuture<?> handlePrepare() {
    throw new IllegalStateException("Missing implementation to handle COMMAND_PREPARE");
  }

  /**
   * Handles calls to {@link Player#stop}.
   *

View on GitHub (pinned to dd430f7053)

Solutions

  1. Override protected ListenableFuture<?> handleSetPlayWhenReady(boolean playWhenReady) in your subclass and return an immediate future (Futures.immediateFuture(null)) after updating state.
  2. If play/pause is not supported, remove COMMAND_PLAY_PAUSE from the commands in your State.
  3. Unit-test every advertised command by calling it against a State built with default commands.

Example fix

// before
public class MyPlayer extends SimpleBasePlayer {
  // no handleSetPlayWhenReady -> play() throws
}

// after
@Override
protected ListenableFuture<?> handleSetPlayWhenReady(boolean playWhenReady) {
  return updateStateAndNotify(
      state.buildUpon().setPlayWhenReady(playWhenReady).build());
}
Defensive patterns

Strategy: validation

Validate before calling

// Before exposing the player, ensure handler exists or command absent:
State state = new State.Builder()
    .setAvailableCommands(buildAvailableCommands()) // omit COMMAND_PLAY_PAUSE if unimplemented
    .build();

Prevention

When it happens

Trigger: A SimpleBasePlayer subclass's State.Builder sets COMMAND_PLAY_PAUSE available (or never overrides isCommandAvailable to exclude it) and the app calls play(), pause(), or setPlayWhenReady(true/false).

Common situations: Writing a custom SimpleBasePlayer and forgetting one handler; copying available-command sets from an example; adding a command to availableCommands without implementing its handler.

Related errors


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