[GH-ISSUE #1617] Reconnection issues after suspend #738

Closed
opened 2026-02-27 19:32:12 +03:00 by kerem · 2 comments
Owner

Originally created by @zappolowski on GitHub (Oct 24, 2025).
Original GitHub issue: https://github.com/librespot-org/librespot/issues/1617

Description

Librespot fails to reconnect after system suspend, tries reconnecting indefinitely.

Version

Latest dev (9142e6941b as time of writing).

How to reproduce

  1. launch librespot with OAuth or zeroconf (user name and password authentication wasn't tested, but I would assume to behave the same)
  2. wait until connection is done, maybe play some music
  3. suspend the system for more than an hour (default expiration of the token)

Log

[2025-10-24T05:02:36Z ERROR librespot_core::dealer] Error while connecting: Invalid state { HTTP error: 401 Unauthorized }

That log line is repeated until the process is stopped (or maybe after an hour has passed, haven't tested that one).

Host

  • OS: Linux
  • Platform: x86_64

Additional context

I tracked it down to librespot_core::token::Token::is_expired(&self). This uses std::time::Instant to check whether a token is expired. On Linux std::time::Instant uses CLOCK_MONOTONIC (see here) which is paused during suspend and thus that check still considers the token to be valid while in fact it's already expired. I added some logging to prove this

[2025-10-23T21:23:33Z INFO  librespot_core::token] checking is_expired with now = Instant {
        tv_sec: 164174,
        tv_nsec: 460124698,
    }

... system suspended

[2025-10-24T05:02:05Z INFO  librespot_core::token] checking is_expired with now = Instant {
        tv_sec: 164204,
        tv_nsec: 461343662,
    }

The system was suspended for about eight hours, but for the check only thirty seconds have passed.

I got it to work for me by switching to std::time::SystemTime, but this has issues by being not monotonically increasing. Best would be to use CLOCK_BOOTTIME on Linux, which doesn't seem feasible at the moment, but I found boot-time, which could be a drop-in replacement for this use case.

Originally created by @zappolowski on GitHub (Oct 24, 2025). Original GitHub issue: https://github.com/librespot-org/librespot/issues/1617 ### Description Librespot fails to reconnect after system suspend, tries reconnecting indefinitely. ### Version Latest dev (9142e6941b5b827422324ccc35c08093c3dd70fd as time of writing). ### How to reproduce 1. launch `librespot` with OAuth or zeroconf (user name and password authentication wasn't tested, but I would assume to behave the same) 2. wait until connection is done, maybe play some music 3. suspend the system for more than an hour (default expiration of the token) ### Log ``` [2025-10-24T05:02:36Z ERROR librespot_core::dealer] Error while connecting: Invalid state { HTTP error: 401 Unauthorized } ``` That log line is repeated until the process is stopped (or maybe after an hour has passed, haven't tested that one). ### Host - OS: Linux - Platform: x86_64 ### Additional context I tracked it down to [`librespot_core::token::Token::is_expired(&self)`](https://github.com/librespot-org/librespot/blob/dev/core/src/token.rs#L122). This uses `std::time::Instant` to check whether a token is expired. On Linux `std::time::Instant` uses `CLOCK_MONOTONIC` (see [here](https://github.com/rust-lang/rust/blob/27050c0d15af664cf43ce4b0badec230e0bfcac5/library/std/src/sys/pal/unix/time.rs#L264)) which is paused during suspend and thus that check still considers the token to be valid while in fact it's already expired. I added some logging to prove this ``` [2025-10-23T21:23:33Z INFO librespot_core::token] checking is_expired with now = Instant { tv_sec: 164174, tv_nsec: 460124698, } ... system suspended [2025-10-24T05:02:05Z INFO librespot_core::token] checking is_expired with now = Instant { tv_sec: 164204, tv_nsec: 461343662, } ``` The system was suspended for about eight hours, but for the check only thirty seconds have passed. I got it to work for me by switching to `std::time::SystemTime`, but this has issues by being not monotonically increasing. Best would be to use `CLOCK_BOOTTIME` on Linux, which doesn't seem feasible at the moment, but I found [`boot-time`](https://crates.io/crates/boot-time), which could be a drop-in replacement for this use case.
kerem 2026-02-27 19:32:12 +03:00
  • closed this issue
  • added the
    bug
    label
Author
Owner

@roderickvd commented on GitHub (Oct 24, 2025):

Oh wow good one. When you changed to SystemTime, what did you use: elapsed? Though boot-time seems relatively lightweight from the outset, if we can manage to stay within std that’d be nice, and I wonder if SystemTime not monotically increasing will actually be a problem.

<!-- gh-comment-id:3441199075 --> @roderickvd commented on GitHub (Oct 24, 2025): Oh wow good one. When you changed to `SystemTime`, what did you use: `elapsed`? Though `boot-time` seems relatively lightweight from the outset, if we can manage to stay within `std` that’d be nice, and I wonder if `SystemTime` *not* monotically increasing will actually be a problem.
Author
Owner

@zappolowski commented on GitHub (Oct 24, 2025):

For testing I just replaced calls to Instant::now() with SystemTime::now() and adjusted the type of the field.

<!-- gh-comment-id:3441216211 --> @zappolowski commented on GitHub (Oct 24, 2025): For testing I just replaced calls to `Instant::now()` with `SystemTime::now()` and adjusted the type of the field.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/librespot#738
No description provided.