From 2cd1d5efb155f477376941a57cfd67143b59e084 Mon Sep 17 00:00:00 2001 From: user Date: Sun, 22 Feb 2026 05:45:00 +0100 Subject: [PATCH] fix: race condition in skip/seek/stop losing track state task.cancel() triggers _play_loop's finally block asynchronously. When cmd_skip or cmd_seek called _ensure_loop before the finally block ran, the old task's cleanup would overwrite the new task's state -- causing !np to report "Nothing playing" while audio was still streaming. Now await the cancelled task before restarting the loop, ensuring the finally block completes first. Co-Authored-By: Claude Opus 4.6 --- plugins/music.py | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/plugins/music.py b/plugins/music.py index ba37903..0d82fa9 100644 --- a/plugins/music.py +++ b/plugins/music.py @@ -537,10 +537,14 @@ async def cmd_stop(bot, message): task = ps.get("task") if task and not task.done(): task.cancel() - ps["current"] = None - ps["task"] = None - ps["done_event"] = None - ps["duck_vol"] = None + try: + await task + except (asyncio.CancelledError, Exception): + pass + else: + ps["current"] = None + ps["task"] = None + ps["duck_vol"] = None await bot.reply(message, "Stopped") @@ -593,14 +597,15 @@ async def cmd_skip(bot, message): await bot.reply(message, "Nothing playing") return + skipped = ps["current"] + task = ps.get("task") if task and not task.done(): task.cancel() - - skipped = ps["current"] - ps["current"] = None - ps["task"] = None - ps["duck_vol"] = None + try: + await task + except (asyncio.CancelledError, Exception): + pass if ps["queue"]: _ensure_loop(bot) @@ -657,13 +662,14 @@ async def cmd_seek(bot, message): # Re-insert current track at front of queue (local_path intact) ps["queue"].insert(0, track) - # Cancel the play loop + # Cancel the play loop and wait for cleanup task = ps.get("task") if task and not task.done(): task.cancel() - ps["current"] = None - ps["task"] = None - ps["duck_vol"] = None + try: + await task + except (asyncio.CancelledError, Exception): + pass _ensure_loop(bot, seek=target) await bot.reply(message, f"Seeking to {_fmt_time(target)}")