Repository navigation
Description
What happened
When I pause from outside spotatui (playerctl, Quickshell's media widget, media keys), the audio stops, but spotatui still thinks it's playing. Its own playbar shows Playing, and over MPRIS it keeps reporting PlaybackStatus as Playing, so desktop widgets show the wrong icon and a frozen position. Shortly after, the TUI shows Failed to start native playback.
Pausing with spotatui's own key works: the TUI, playerctl status and Quickshell all switch to Paused.
playerctl -p spotatui pause and playerctl -p spotatui play-pause both trigger it.
The two pause paths do different amounts of work. The TUI key goes through pause_playback() in src/infra/network/playback.rs, which pauses the player and also clears pending_start_playback and native_load_watchdog, cancels play-after-transfer, and sets native_is_playing and current_playback_context.is_playing to false. The MPRIS handlers in src/runtime/startup.rs (MprisEvent::Pause, PlayPause, Stop) only call player.pause() and set_native_playback_intent(false), so the app state still says playing.
The Failed to start native playback error may come from the play-after-transfer flag the MPRIS path doesn't cancel. The PlayerEvent::Paused handler in src/infra/player/events.rs checks take_play_after_transfer() first. If the flag is set, it calls player.play() and skips mpris.set_playback_status(false). I haven't confirmed this.
Expected: pausing over MPRIS behaves the same as pausing in the TUI. One shared App method for the pause bookkeeping, called from both paths, would cover it.
Steps to reproduce
- Start spotatui with native streaming and play a song.
- Run playerctl -p spotatui pause (or play-pause).
- The audio stops, but the TUI still shows Playing and playerctl -p spotatui status prints Playing.
- Shortly after, Failed to start native playback appears.
Frontend
Terminal app
Source
Spotify
spotatui version
0.47.0
OS
Fedora 44
How you installed spotatui
cargo install / cargo binstall
Terminal
Kitty
Debug log
The MPRIS Pause/PlayPause path writes nothing to the log, so the pause itself
doesn't appear. In-app toggles do log ("toggling playback: ..."). This is the snippet from
a run where the error appeared:
[01:17:10][spotatui::core::app::transport][INFO] toggling playback: paused
[01:17:36][spotatui::core::app::transport][INFO] toggling playback: playing
[01:17:49][spotatui::infra::network::playback][INFO] starting native playback via direct load route
[01:17:49][spotatui_librespot_connect::spirc][WARN] SpircCommand::Activate will be ignored while already active
[01:17:54][spotatui::core::app::tick][WARN] no player event within 5s of native load; forcing recovery attempt 1
[01:17:54][spotatui::infra::player::events][INFO] attempting native streaming recovery
[01:17:54][spotatui::infra::player::events][INFO] recovery route: replay parked StartPlayback
[01:17:55][spotatui::infra::network::playback][INFO] recovery replay: starting native playback via Spotify context route on device <device>
[01:17:58][spotatui_librespot_connect::spirc][ERROR] failed to handle request: Invalid state { context is not available. type: Default }
[01:17:58][spotatui::infra::network::requests][WARN] Spotify API PUT /v1/me/player/play?device_id=<device> failed in 3043ms: Spotify API 404 Not Found failed: Requested entity was not found
[01:17:58][spotatui::core::app::status][INFO] error occurred: Failed to start native playback: Spotify API 404 Not Found ... { Response status code: 410 Gone }
Activity
spotatui commentedon Nov 6, 2026
🎧 spotatui triage
Auto-triaged with
claude-opus-5-5.Label:
bugLikely area: MPRIS integration and native streaming pause bookkeeping (runtime/startup.rs MPRIS loop vs infra/network pause_playback)
Summary
If you pause native Spotify streaming from outside spotatui (playerctl, a desktop media widget or media keys), the audio stops but the app still thinks it is playing. The TUI playbar and the MPRIS PlaybackStatus both stay on Playing, desktop widgets show a frozen position, and soon after the TUI shows "Failed to start native playback". Pausing with spotatui's own key works. The code matches the reporter's reading: the MPRIS pause arms do much less bookkeeping than the in-app pause path. This is not fixed in the changelog.
Key points
playerctl -p spotatui pauseandplay-pauseboth stop the audio, but the TUI andplayerctl statusstill say Playing.Where to look
src/runtime/startup.rs: The MPRIS event loop. Its Pause and Stop arms only call player.pause() and set_native_playback_intent(false). PlayPause only calls pause() or play(). The decoded-source path instead dispatches IoEventsrc/infra/network/playback.rs: pause_playback() clears pending_start_playback and native_load_watchdog, calls cancel_play_after_transfer(), and sets native_is_playing and ctx.is_playing to false. The MPRIS path skips all of this.src/infra/player/events.rs: The PlayerEvent::Paused handler checks take_play_after_transfer() first. If the flag is set, it calls player.play() and skips the shared_is_playing store, the MPRIS status update and the App state updWhat would help
Possible duplicates: none found
Compared against 30 open and 100 recently closed issues.
Radhik-Amp commentedon Nov 6, 2026
After some digging, I found a bug where librespot's player thread hangs inside the ALSA drain when it pauses. I'm not too familiar, but when AlsaSink::stop() calls pcm.drain(), it never returns on PipeWire's ALSA plugin. It looks like handle_pause() stops the sink before it sends PlayerEvent::Paused.
The MPRIS arms in startup.rs still use a bare player.pause() / player.play() instead of pause_native_playback(), which src/core/app/AGENTS.md asks every pause to go through. I have a small change that routes Play/Pause/PlayPause/Stop through pause_native_playback() and a new resume_native_playback() (pulled out of toggle_playback()), with tests. Would you like that as a PR on its own? I'm not too confident with the drain behavior, but this is just my experience. I could also make a more detailed issue as well. Thanks.