diff --git a/crates/uv/src/commands/auth/login.rs b/crates/uv/src/commands/auth/login.rs index c0d937e7c..3e708c7b0 100644 --- a/crates/uv/src/commands/auth/login.rs +++ b/crates/uv/src/commands/auth/login.rs @@ -19,6 +19,9 @@ use crate::commands::ExitStatus; use crate::printer::Printer; use crate::settings::NetworkSettings; +// We retry no more than this many times when polling for login status. +const STATUS_RETRY_LIMIT: u32 = 60; + /// Login to a service. pub(crate) async fn login( service: Service, @@ -215,6 +218,7 @@ pub(crate) async fn pyx_login_with_browser( url }; + let mut retry = 0; let credentials = loop { let response = client .for_host(store.api()) @@ -225,6 +229,7 @@ pub(crate) async fn pyx_login_with_browser( // Retry on 404. reqwest::StatusCode::NOT_FOUND => { tokio::time::sleep(std::time::Duration::from_secs(1)).await; + retry += 1; } // Parse the credentials on success. _ if response.status().is_success() => { @@ -236,6 +241,12 @@ pub(crate) async fn pyx_login_with_browser( break Err(anyhow::anyhow!("Failed to login with code `{status}`")); } } + + if retry >= STATUS_RETRY_LIMIT { + break Err(anyhow::anyhow!( + "Login session timed out after {STATUS_RETRY_LIMIT} seconds" + )); + } }?; store.write(&credentials).await?;