Skip to content

MPRIS Play/Pause stops audio but spotatui keeps showing and reporting playing #985

Open

Description

@Radhik-Amp

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

  1. Start spotatui with native streaming and play a song.
  2. Run playerctl -p spotatui pause (or play-pause).
  3. The audio stops, but the TUI still shows Playing and playerctl -p spotatui status prints Playing.
  4. 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

  1. spotatui commented on Nov 6, 2026

    @spotatui
    Contributor

    🎧 spotatui triage

    Auto-triaged with claude-opus-5-5.

    Label: bug
    Likely 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

    • spotatui 0.47.0, terminal app, Spotify source with native streaming, Fedora 44, Kitty, installed with cargo install or binstall.
    • playerctl -p spotatui pause and play-pause both stop the audio, but the TUI and playerctl status still say Playing.
    • Shortly after, the status line shows "Failed to start native playback". The log shows a forced recovery after 5s with no player event, then a context replay that ends in a 404.
    • Pausing with spotatui's own key updates the TUI, playerctl and Quickshell to Paused correctly.
    • The MPRIS pause path writes nothing to the log. The reporter suspects that an uncancelled play-after-transfer flag makes the Paused event handler call play() again, but has not confirmed it.

    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 IoEvent
    • src/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 upd

    What would help

    • A full --debug log of one run, from the MPRIS pause until the error appears.
    • Whether the song was started inside spotatui or handed over from another Spotify Connect device first, which would arm play-after-transfer.
    • Whether the bug also happens when no other Spotify client or device is active.

    Possible duplicates: none found

    Compared against 30 open and 100 recently closed issues.

  2. Radhik-Amp commented on Nov 6, 2026

    @Radhik-Amp
    Author

    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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions