{"record":{"id":"34032f4d59617fca","repo":"google/ExoPlayer","slug":"missing-implementation-to-handle-command-release","errorCode":null,"errorMessage":"Missing implementation to handle COMMAND_RELEASE","messagePattern":"Missing implementation to handle COMMAND_RELEASE","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"library/common/src/main/java/com/google/android/exoplayer2/SimpleBasePlayer.java","lineNumber":2966,"sourceCode":"   * @return A {@link ListenableFuture} indicating the completion of all immediate {@link State}\n   *     changes caused by this call.\n   */\n  @ForOverride\n  protected ListenableFuture<?> handleStop() {\n    throw new IllegalStateException(\"Missing implementation to handle COMMAND_STOP\");\n  }\n\n  /**\n   * Handles calls to {@link Player#release}.\n   *\n   * <p>Will only be called if {@link Player#COMMAND_RELEASE} is available.\n   *\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<?> handleRelease() {\n    throw new IllegalStateException(\"Missing implementation to handle COMMAND_RELEASE\");\n  }\n\n  /**\n   * Handles calls to {@link Player#setRepeatMode}.\n   *\n   * <p>Will only be called if {@link Player#COMMAND_SET_REPEAT_MODE} is available.\n   *\n   * @param repeatMode The requested {@link RepeatMode}.\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<?> handleSetRepeatMode(@RepeatMode int repeatMode) {\n    throw new IllegalStateException(\"Missing implementation to handle COMMAND_SET_REPEAT_MODE\");\n  }\n\n  /**\n   * Handles calls to {@link Player#setShuffleModeEnabled}.","sourceCodeStart":2948,"sourceCodeEnd":2984,"githubUrl":"https://github.com/google/ExoPlayer/blob/dd430f7053a1a3958deea3ead6a0565150c06bfc/library/common/src/main/java/com/google/android/exoplayer2/SimpleBasePlayer.java#L2948-L2984","documentation":"Default handler for Player.release() in SimpleBasePlayer: IllegalStateException thrown because handleRelease() is not overridden although COMMAND_RELEASE is available in the current State. Every command the player advertises must be backed by an implementation.","triggerScenarios":"Calling player.release() on a SimpleBasePlayer subclass that did not override handleRelease() while its State includes COMMAND_RELEASE.","commonSituations":"Custom player implementations that clean up resources elsewhere (e.g. in a close() method) but forget the handler; test harnesses that always call release().","solutions":["Override handleRelease() to tear down your backend and return a future completed when fully released; also set STATE_IDLE per the documented contract.","Alternatively remove COMMAND_RELEASE from availableCommands — but note most clients unconditionally call release(), so implementing it is usually correct.","Make handleRelease idempotent so double-release does not fail."],"exampleFix":"// before\n// subclass without handleRelease -> player.release() throws\n\n// after\n@Override\nprotected ListenableFuture<?> handleRelease() {\n  backend.shutdown();\n  return updateStateAndNotify(\n      new State.Builder().setPlaybackState(STATE_IDLE).setPlaylist(Collections.emptyList()).build());\n}","handlingStrategy":"validation","validationCode":"if (!supportsRelease) commands.remove(Player.COMMAND_RELEASE); // rarely correct — most clients call release()","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always implement handleRelease() when exposing a SimpleBasePlayer.","Make release idempotent and set STATE_IDLE.","Call player.release() in unit tests of your custom player."],"tags":["simplebaseplayer","custom-player","release","media3","contract"],"backgroundTag":null,"analyzedSha":"dd430f7053a1a3958deea3ead6a0565150c06bfc","analyzedAt":"2026-08-14T12:22:02.982Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}