Skip to content

Fix silent auto-skip and recover playback instead of dropping the queue - #130

Closed
Skila1 wants to merge 8 commits into
ChocoMeow:mainfrom
Skila1:fix/playback-reliability
Closed

Skila1 wants to merge 8 commits into
ChocoMeow:mainfrom
Skila1:fix/playback-reliability

Conversation

@Skila1

@Skila1 Skila1 commented Aug 19, 2026

Copy link
Copy Markdown

Problem

On Lavalink loadFailed / TrackException, Vocard treated every TrackEnd as "play the next song". A YouTube (or other source) failure could skip the current track, then keep consuming later queue items, with no useful user message.

Symptom

  • Track fails to start
  • Bot retries or appears to skip with no explanation
  • Following tracks can disappear from the queue
  • After a node blip, playback could die or advance incorrectly

Root cause

on_voicelink_track_end always called do_next(), ignoring reason. There was no single owner for "this logical queue item is still the current attempt", so TrackStuck / TrackException / TrackEnd / a failed play PATCH could each advance the queue. Lavalink v4 close frames and NodeNotAvailable were also handled as generic failures.

Before

  • loadFailed → skip to next item (and sometimes more)
  • Node/websocket drops could burn the queue
  • Users got no reliable "couldn't play this track" message (later sends went through the original slash interaction webhook, which expires)

After

  • One queue consumer, reason-aware TrackEnd
  • Retry the same logical item once, then one QUEUE_ADVANCE
  • Stale events / stale PATCHes cannot resurrect or double-advance
  • Transient NodeNotAvailable recovers in place (no retry burn, no advance)
  • WebSocket CLOSE frames are not passed to json.loads
  • Attempt-task cleanup never cancels itself
  • After exhaustion: one Discord notice on the request text channel (Couldn't play **TITLE**. Skipping to the next track.)
  • Playback health classification for the dashboard (optional)

YouTube/source outages are surfaced (health + user notice) instead of silently skipping through the queue. This does not change Lavalink/YouTube plugin configuration.

Compatibility

Unrelated behavior is intentionally unchanged: /play, skip, forceplay, back, skipTo, controller jump, repeat TRACK/QUEUE, autoplay, request channel, session restore, IPC, Mongo history, start/end offsets, controller refresh, voice status, votes, inactive timer. Player.current remains a Track; QueueItem is internal.

Tests

107 passed (tests/: playback transitions, notifications, tasks, health, Lavalink websocket).

Skila1 and others added 8 commits August 20, 2026 06:12
…ate machine.

Failed tracks now retry the same logical item instead of consuming the next song, while preserving upstream start/end offsets, listen history, and caller TRACK-loop behavior.

Co-authored-by: Cursor <cursoragent@cursor.com>
Bind seq from the stale-op snapshot so queue advancement after a failed retry no longer crashes.

Co-authored-by: Cursor <cursoragent@cursor.com>
Keep the TRACK_EXCEPTION grep key and leave playback policy unchanged.

Co-authored-by: Cursor <cursoragent@cursor.com>
… changing playback.

Surface installed plugin versions from GET /v4/info and escalate only repeated source-wide errors, so admins can tell a source/node outage from a broken bot.

Co-authored-by: Cursor <cursoragent@cursor.com>
Keep the distinct-item window for persistence and recovery, so track-specific errors still do not mark YouTube unhealthy.

Co-authored-by: Cursor <cursoragent@cursor.com>
…g playback recovery.

Co-authored-by: Cursor <cursoragent@cursor.com>
…g warnings.

Co-authored-by: Cursor <cursoragent@cursor.com>
… request text channel.

Co-authored-by: Cursor <cursoragent@cursor.com>
@Skila1

Skila1 commented Aug 19, 2026

Copy link
Copy Markdown
Author

Hey, I ran into an issue with Vocard silently eating through the queue when Lavalink failed a track, so I ended up fixing the playback/retry handling and a few related issues I found while live testing it. (WARNING: heavy AI usage, doesnt bother me personally)

107 tests passing. If you want any of it upstream, feel free to merge it, cherry-pick what you want, or ask me to split anything out. If not, all good, I'm running the fork for myself anyway.

@ChocoMeow

Copy link
Copy Markdown
Owner

Thank you for the pull request and for working on improving queue stability! However, we can't accept this PR in its current state. Please review the feedback below before resubmitting:

  • Please change the target branch of your pull request to beta instead of main.
  • The scope of this PR is too large. Please break these changes down into smaller, focused pull requests organized by specific topics.
  • The issue regarding users not receiving detailed error messages isn't something that can be resolved in Vocard. Lavalink only returns a generic "Something went wrong while playing" error without actionable details, limiting what we can report.
  • Retrying a stuck track isn't necessarily better than skipping it. If a track is stuck due to a source issue, retrying stalls the queue longer, whereas skipping allows the user to continue listening to playable tracks.

@ChocoMeow

Copy link
Copy Markdown
Owner

and the new test cases seem aimed at testing Lavalink itself rather than Vocard code. We should stick to tests that cover Vocard's own functions.

@Skila1

Skila1 commented Aug 20, 2026

Copy link
Copy Markdown
Author

Thanks for the review notes — targeting beta and splitting this into smaller PRs was the right call.

This PR was larger than it should have been, so I am closing it in favor of focused beta replacements:

Later slices (optional notices, optional health IPC, optional stuck retry) will be opened against beta only after the queue consumer lands, so they do not duplicate that diff.

My personal fix/playback-reliability fork is unchanged and still has the everything-enabled behavior for my own deploy.

@Skila1 Skila1 closed this Aug 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants