diff --git a/DOCS/interface-changes.rst b/DOCS/interface-changes.rst
index 9048a29a3a..7dcaf5c8eb 100644
--- a/DOCS/interface-changes.rst
+++ b/DOCS/interface-changes.rst
@@ -21,6 +21,7 @@ Interface changes
 
  --- mpv 0.27.0 ---
     - drop previously deprecated --field-dominance option
+    - drop previously deprecated "osd" command
  --- mpv 0.26.0 ---
     - remove remaining deprecated audio device options, like --alsa-device
       Some of them were removed in earlier releases.
diff --git a/input/cmd_list.c b/input/cmd_list.c
index bc82e687fd..6199e7c65f 100644
--- a/input/cmd_list.c
+++ b/input/cmd_list.c
@@ -98,9 +98,6 @@ const struct mp_cmd_def mp_cmds[] = {
   { MP_CMD_PLAYLIST_SHUFFLE, "playlist-shuffle", },
   { MP_CMD_SUB_STEP, "sub-step", { ARG_INT }, .allow_auto_repeat = true },
   { MP_CMD_SUB_SEEK, "sub-seek", { ARG_INT }, .allow_auto_repeat = true },
-#if HAVE_GPL
-  { MP_CMD_OSD, "osd", { OARG_INT(-1) } },
-#endif
   { MP_CMD_PRINT_TEXT, "print-text", { ARG_STRING }, .allow_auto_repeat = true },
   { MP_CMD_SHOW_TEXT, "show-text", { ARG_STRING, OARG_INT(-1), OARG_INT(0) },
     .allow_auto_repeat = true},
diff --git a/input/cmd_list.h b/input/cmd_list.h
index 5d456c033e..6590158c31 100644
--- a/input/cmd_list.h
+++ b/input/cmd_list.h
@@ -46,7 +46,6 @@ enum mp_command_type {
     MP_CMD_QUIT_WATCH_LATER,
     MP_CMD_PLAYLIST_NEXT,
     MP_CMD_PLAYLIST_PREV,
-    MP_CMD_OSD,
     MP_CMD_SCREENSHOT,
     MP_CMD_SCREENSHOT_TO_FILE,
     MP_CMD_SCREENSHOT_RAW,
diff --git a/player/command.c b/player/command.c
index c328d77ce8..e9a321627f 100644
--- a/player/command.c
+++ b/player/command.c
@@ -5194,28 +5194,6 @@ int run_command(struct MPContext *mpctx, struct mp_cmd *cmd, struct mpv_node *re
         break;
     }
 
-#if HAVE_GPL
-    // Potentially GPL due to 8d190244d21a4d40bb9e8f7d51aa09ca1888de09.
-    case MP_CMD_OSD: {
-        MP_WARN(mpctx, "The 'osd' command is deprecated. "
-                       "Use 'cycle osd-level' instead.\n");
-        int v = cmd->args[0].v.i;
-#define MAX_OSD_LEVEL 3
-        if (opts->osd_level > MAX_OSD_LEVEL)
-            opts->osd_level = MAX_OSD_LEVEL;
-        if (v < 0)
-            opts->osd_level = (opts->osd_level + 1) % (MAX_OSD_LEVEL + 1);
-        else
-            opts->osd_level = MPCLAMP(v, 0, MAX_OSD_LEVEL);
-        if (opts->osd_level > 0 && (on_osd & MP_ON_OSD_MSG))
-            set_osd_msg(mpctx, osdl, osd_duration, "OSD level: %d", opts->osd_level);
-        if (opts->osd_level == 0)
-            set_osd_msg(mpctx, 0, 0, "");
-        mp_wakeup_core(mpctx);
-        break;
-    }
-#endif
-
     case MP_CMD_PRINT_TEXT: {
         MP_INFO(mpctx, "%s\n", cmd->args[0].v.s);
         break;