From d1c049dc6b2729c5d52a01cd05d2147e21c049ca Mon Sep 17 00:00:00 2001 From: Anoop Rehman <93463679+anoop-rehman@users.noreply.github.com> Date: Tue, 2 Dec 2025 20:00:08 -0500 Subject: [PATCH 1/3] Clarify UV_HTTP_TIMEOUT format in error message --- crates/uv-client/src/registry_client.rs | 2 +- crates/uv-distribution/src/distribution_database.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/uv-client/src/registry_client.rs b/crates/uv-client/src/registry_client.rs index 79b6025f2..257daab80 100644 --- a/crates/uv-client/src/registry_client.rs +++ b/crates/uv-client/src/registry_client.rs @@ -1207,7 +1207,7 @@ impl RegistryClient { std::io::Error::new( std::io::ErrorKind::TimedOut, format!( - "Failed to download distribution due to network timeout. Try increasing UV_HTTP_TIMEOUT (current value: {}s).", + "Failed to download distribution due to network timeout ({}s).\nTry increasing UV_HTTP_TIMEOUT to a larger integer value (in seconds), e.g., UV_HTTP_TIMEOUT=60", self.timeout().as_secs() ), ) diff --git a/crates/uv-distribution/src/distribution_database.rs b/crates/uv-distribution/src/distribution_database.rs index ef2227df6..ff267c13e 100644 --- a/crates/uv-distribution/src/distribution_database.rs +++ b/crates/uv-distribution/src/distribution_database.rs @@ -93,7 +93,7 @@ impl<'a, Context: BuildContext> DistributionDatabase<'a, Context> { io::Error::new( io::ErrorKind::TimedOut, format!( - "Failed to download distribution due to network timeout. Try increasing UV_HTTP_TIMEOUT (current value: {}s).", + "Failed to download distribution due to network timeout ({}s).\nTry increasing UV_HTTP_TIMEOUT to a larger integer value (in seconds), e.g., UV_HTTP_TIMEOUT=60", self.client.unmanaged.timeout().as_secs() ), ) From 098b71fc932f156201aa2939775a46d297bccc4a Mon Sep 17 00:00:00 2001 From: Anoop Rehman <93463679+anoop-rehman@users.noreply.github.com> Date: Tue, 2 Dec 2025 22:03:54 -0500 Subject: [PATCH 2/3] Emit a helpful error message if user sets a duration environment variable with units --- crates/uv-settings/src/lib.rs | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/crates/uv-settings/src/lib.rs b/crates/uv-settings/src/lib.rs index 6b9b8e98f..c9e8bcadb 100644 --- a/crates/uv-settings/src/lib.rs +++ b/crates/uv-settings/src/lib.rs @@ -602,11 +602,12 @@ impl EnvironmentOptions { pub fn new() -> Result { // Timeout options, matching https://doc.rust-lang.org/nightly/cargo/reference/config.html#httptimeout // `UV_REQUEST_TIMEOUT` is provided for backwards compatibility with v0.1.6 - let http_timeout = parse_integer_environment_variable(EnvVars::UV_HTTP_TIMEOUT)? + let http_timeout = parse_integer_environment_variable(EnvVars::UV_HTTP_TIMEOUT, true)? .or(parse_integer_environment_variable( EnvVars::UV_REQUEST_TIMEOUT, + true, )?) - .or(parse_integer_environment_variable(EnvVars::HTTP_TIMEOUT)?) + .or(parse_integer_environment_variable(EnvVars::HTTP_TIMEOUT, true)?) .map(Duration::from_secs); Ok(Self { @@ -618,9 +619,9 @@ impl EnvironmentOptions { EnvVars::UV_PYTHON_INSTALL_REGISTRY, )?, concurrency: Concurrency { - downloads: parse_integer_environment_variable(EnvVars::UV_CONCURRENT_DOWNLOADS)?, - builds: parse_integer_environment_variable(EnvVars::UV_CONCURRENT_BUILDS)?, - installs: parse_integer_environment_variable(EnvVars::UV_CONCURRENT_INSTALLS)?, + downloads: parse_integer_environment_variable(EnvVars::UV_CONCURRENT_DOWNLOADS, false)?, + builds: parse_integer_environment_variable(EnvVars::UV_CONCURRENT_BUILDS, false)?, + installs: parse_integer_environment_variable(EnvVars::UV_CONCURRENT_INSTALLS, false)?, }, install_mirrors: PythonInstallMirrors { python_install_mirror: parse_string_environment_variable( @@ -636,12 +637,13 @@ impl EnvironmentOptions { log_context: parse_boolish_environment_variable(EnvVars::UV_LOG_CONTEXT)?, upload_http_timeout: parse_integer_environment_variable( EnvVars::UV_UPLOAD_HTTP_TIMEOUT, + true, )? .map(Duration::from_secs) .or(http_timeout) .unwrap_or(Duration::from_secs(15 * 60)), http_timeout: http_timeout.unwrap_or(Duration::from_secs(30)), - http_retries: parse_integer_environment_variable(EnvVars::UV_HTTP_RETRIES)? + http_retries: parse_integer_environment_variable(EnvVars::UV_HTTP_RETRIES, false)? .unwrap_or(uv_client::DEFAULT_RETRIES), #[cfg(feature = "tracing-durations-export")] tracing_durations_file: parse_path_environment_variable( @@ -651,6 +653,7 @@ impl EnvironmentOptions { } } + /// Parse a boolean environment variable. /// /// Adapted from Clap's `BoolishValueParser` which is dual licensed under the MIT and Apache-2.0. @@ -724,7 +727,12 @@ fn parse_string_environment_variable(name: &'static str) -> Result(name: &'static str) -> Result, Error> +/// Parse an integer environment variable. +/// +/// If `is_duration` is `true`, this function will check if the value ends with "s" and provide +/// a helpful error message, as duration environment variables expect an integer value in seconds +/// (not a string like "60s"). +fn parse_integer_environment_variable(name: &'static str, is_duration: bool) -> Result, Error> where T: std::str::FromStr + Copy, ::Err: std::fmt::Display, @@ -746,6 +754,17 @@ where return Ok(None); } + // For duration variables, check if the value ends with "s" and provide a helpful error message + if is_duration && value.trim_end().ends_with('s') { + return Err(Error::InvalidEnvironmentVariable { + name: name.to_string(), + value: value.clone(), + err: format!( + "expected an integer value in seconds (e.g., 30), not a string with units (e.g., 30s)" + ), + }); + } + match value.parse::() { Ok(v) => Ok(Some(v)), Err(err) => Err(Error::InvalidEnvironmentVariable { From 1d86d8585b1afabd1cea1f329bf4fb6100fda671 Mon Sep 17 00:00:00 2001 From: Anoop Rehman <93463679+anoop-rehman@users.noreply.github.com> Date: Wed, 3 Dec 2025 04:05:50 -0500 Subject: [PATCH 3/3] Lint, replace useless format!() --- crates/uv-settings/src/lib.rs | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/crates/uv-settings/src/lib.rs b/crates/uv-settings/src/lib.rs index c9e8bcadb..44c585e12 100644 --- a/crates/uv-settings/src/lib.rs +++ b/crates/uv-settings/src/lib.rs @@ -607,7 +607,10 @@ impl EnvironmentOptions { EnvVars::UV_REQUEST_TIMEOUT, true, )?) - .or(parse_integer_environment_variable(EnvVars::HTTP_TIMEOUT, true)?) + .or(parse_integer_environment_variable( + EnvVars::HTTP_TIMEOUT, + true, + )?) .map(Duration::from_secs); Ok(Self { @@ -619,9 +622,15 @@ impl EnvironmentOptions { EnvVars::UV_PYTHON_INSTALL_REGISTRY, )?, concurrency: Concurrency { - downloads: parse_integer_environment_variable(EnvVars::UV_CONCURRENT_DOWNLOADS, false)?, + downloads: parse_integer_environment_variable( + EnvVars::UV_CONCURRENT_DOWNLOADS, + false, + )?, builds: parse_integer_environment_variable(EnvVars::UV_CONCURRENT_BUILDS, false)?, - installs: parse_integer_environment_variable(EnvVars::UV_CONCURRENT_INSTALLS, false)?, + installs: parse_integer_environment_variable( + EnvVars::UV_CONCURRENT_INSTALLS, + false, + )?, }, install_mirrors: PythonInstallMirrors { python_install_mirror: parse_string_environment_variable( @@ -653,7 +662,6 @@ impl EnvironmentOptions { } } - /// Parse a boolean environment variable. /// /// Adapted from Clap's `BoolishValueParser` which is dual licensed under the MIT and Apache-2.0. @@ -728,11 +736,14 @@ fn parse_string_environment_variable(name: &'static str) -> Result(name: &'static str, is_duration: bool) -> Result, Error> +fn parse_integer_environment_variable( + name: &'static str, + is_duration: bool, +) -> Result, Error> where T: std::str::FromStr + Copy, ::Err: std::fmt::Display, @@ -759,9 +770,7 @@ where return Err(Error::InvalidEnvironmentVariable { name: name.to_string(), value: value.clone(), - err: format!( - "expected an integer value in seconds (e.g., 30), not a string with units (e.g., 30s)" - ), + err: "expected an integer value in seconds (e.g., 30), not a string with units (e.g., 30s)".to_string(), }); }