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 <= {@code
* mediaItemIndex} < {@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
- 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.
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
- 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.
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
- Missing implementation to handle COMMAND_SET_VOLUME
- Missing implementation to handle COMMAND_SET_DEVICE_VOLUME o
- Missing implementation to handle COMMAND_ADJUST_DEVICE_VOLUM
- Missing implementation to handle COMMAND_SET_VIDEO_SURFACE
- Missing implementation to handle COMMAND_SET_MEDIA_ITEM(S)
AI-assisted analysis of google/ExoPlayer@dd430f7053 (2026-08-14).
Data as JSON: /api/errors/e76eebbc23be17cf.
Report an issue: GitHub.