From 0056a99d192bb36408839e42a1f510379c3e98a2 Mon Sep 17 00:00:00 2001 From: LargeModGames <84450916+LargeModGames@users.noreply.github.com> Date: Fri, 12 Jun 2026 15:42:41 +0200 Subject: [PATCH 1/3] fix: self-update silently failing with 403 since v0.38.5 - send User-Agent and Accept headers when downloading release assets for checksum verification (GitHub returns 403 without a User-Agent and JSON metadata without Accept: application/octet-stream) - log auto-update failures instead of silently discarding them - run 'spotatui update' via spawn_blocking so reqwest::blocking cannot panic the async runtime --- src/cli/update.rs | 5 +++++ src/runtime.rs | 26 ++++++++++++++++++-------- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/cli/update.rs b/src/cli/update.rs index 1ad6a15..222b7b3 100644 --- a/src/cli/update.rs +++ b/src/cli/update.rs @@ -112,12 +112,16 @@ fn verify_release_checksum(release: &self_update::update::Release) -> Result<()> ) })?; + // GitHub's API rejects requests without a User-Agent with 403, and asset URLs + // return JSON metadata instead of the file unless Accept is application/octet-stream. let client = reqwest::blocking::Client::builder() + .user_agent(concat!("spotatui/", env!("CARGO_PKG_VERSION"))) .timeout(std::time::Duration::from_secs(60)) .build()?; let checksum_text = client .get(&checksum_asset.download_url) + .header(reqwest::header::ACCEPT, "application/octet-stream") .send()? .error_for_status()? .text()?; @@ -138,6 +142,7 @@ fn verify_release_checksum(release: &self_update::update::Release) -> Result<()> let binary_bytes = client .get(&asset.download_url) + .header(reqwest::header::ACCEPT, "application/octet-stream") .send()? .error_for_status()? .bytes()?; diff --git a/src/runtime.rs b/src/runtime.rs index 524db6b..3b02bc5 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -482,10 +482,12 @@ fn add_self_update_cli(clap_app: ClapApp) -> ClapApp { } #[cfg(feature = "self-update")] -fn handle_self_update_command(matches: &ArgMatches) -> Result { +async fn handle_self_update_command(matches: &ArgMatches) -> Result { if let Some(update_matches) = matches.subcommand_matches("update") { let do_install = update_matches.get_flag("install"); - cli::check_for_update(do_install)?; + // Must use spawn_blocking because self_update uses reqwest::blocking internally, + // which creates its own tokio runtime and panics if called from an async context. + tokio::task::spawn_blocking(move || cli::check_for_update(do_install)).await??; return Ok(true); } @@ -493,7 +495,7 @@ fn handle_self_update_command(matches: &ArgMatches) -> Result { } #[cfg(not(feature = "self-update"))] -fn handle_self_update_command(_matches: &ArgMatches) -> Result { +async fn handle_self_update_command(_matches: &ArgMatches) -> Result { Ok(false) } @@ -513,10 +515,18 @@ async fn run_auto_update(matches: &ArgMatches, user_config: &UserConfig) { let delay_secs = crate::core::user_config::parse_update_delay_secs(&user_config.behavior.auto_update_delay) .unwrap_or(0); - let update_result = tokio::task::spawn_blocking(move || cli::install_update_silent(delay_secs)) - .await - .ok() - .and_then(|r| r.ok()); + let update_result = + match tokio::task::spawn_blocking(move || cli::install_update_silent(delay_secs)).await { + Ok(Ok(outcome)) => Some(outcome), + Ok(Err(e)) => { + log::warn!("auto-update failed: {:#}", e); + None + } + Err(e) => { + log::warn!("auto-update task panicked: {}", e); + None + } + }; match update_result { Some(cli::UpdateOutcome::Installed(new_version)) => { @@ -633,7 +643,7 @@ screens more often and cost more CPU. Animation-heavy views keep their separate } // Handle self-update command (doesn't need Spotify auth) - if handle_self_update_command(&matches)? { + if handle_self_update_command(&matches).await? { return Ok(()); } From 9caf613ddf314f67e64cb328509cc9c75d2f756d Mon Sep 17 00:00:00 2001 From: LargeModGames <84450916+LargeModGames@users.noreply.github.com> Date: Fri, 12 Jun 2026 15:43:35 +0200 Subject: [PATCH 2/3] Add self-update fix announcement --- announcements.json | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/announcements.json b/announcements.json index 0d14a5e..5c18406 100644 --- a/announcements.json +++ b/announcements.json @@ -1,6 +1,15 @@ { "version": 1, "announcements": [ + { + "id": "2026-06-12-self-update-broken", + "title": "Action needed: auto-update is broken in v0.38.5 to v0.39.0", + "body": "A bug in update checksum verification means spotatui v0.38.5 through v0.39.0 cannot update themselves: the update check fails silently on every launch. The fix ships in v0.39.1, but affected versions cannot reach it on their own. Please update once manually (winget upgrade, brew upgrade, your package manager, or the GitHub releases page). Auto-update works again from v0.39.1 onward.", + "level": "warning", + "url": "https://github.com/LargeModGames/spotatui/pull/303", + "starts_at": "2026-06-12T00:00:00Z", + "ends_at": null + }, { "id": "2026-06-12-lua-scripting", "title": "New: Lua plugin scripting", From 47e60a108c465e7ae05056c393a780df6a777c2a Mon Sep 17 00:00:00 2001 From: LargeModGames <84450916+LargeModGames@users.noreply.github.com> Date: Fri, 12 Jun 2026 15:46:16 +0200 Subject: [PATCH 3/3] chore(release): bump version to 0.39.1 and update changelog --- CHANGELOG.md | 6 ++++++ Cargo.lock | 2 +- Cargo.toml | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 464136a..63f94b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## [v0.39.1] 2026-06-12 + +### Fixed + +- **Self-update silently failing since v0.38.5**: Release-asset downloads during update checksum verification now send a `User-Agent` header (GitHub rejects requests without one with `403 Forbidden`) and `Accept: application/octet-stream` (without it the asset API URL returns JSON metadata instead of the file), so auto-update and `spotatui update --install` work again. Auto-update failures are now logged instead of silently discarded, and `spotatui update` runs on a blocking thread so it can no longer panic the async runtime. Clients on v0.38.5 through v0.39.0 carry the broken verification in their own binaries and need one manual update to reach this release ([#303](https://github.com/LargeModGames/spotatui/pull/303)). + ## [v0.39.0] 2026-06-12 ### Added diff --git a/Cargo.lock b/Cargo.lock index 40b979a..e6ee0e6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5593,7 +5593,7 @@ dependencies = [ [[package]] name = "spotatui" -version = "0.39.0" +version = "0.39.1" dependencies = [ "anyhow", "arboard", diff --git a/Cargo.toml b/Cargo.toml index bc50e59..7f27098 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ documentation = "https://github.com/LargeModGames/spotatui" repository = "https://github.com/LargeModGames/spotatui" keywords = ["spotify", "tui", "cli", "terminal"] categories = ["command-line-utilities"] -version = "0.39.0" +version = "0.39.1" authors = ["LargeModGames "] edition = "2021" license = "MIT"