Log the duration we took before erroring (#18231)

We previously errored due to not having long enough backoff, and there
were questions about how long the retries are, so let's log this on
failure.
This commit is contained in:
konsti
2026-03-06 15:51:10 +01:00
committed by GitHub
parent 686f43e67e
commit 401661ee22
9 changed files with 160 additions and 92 deletions
+14 -1
View File
@@ -8,6 +8,7 @@ use std::path::PathBuf;
use std::pin::Pin;
use std::str::FromStr;
use std::task::{Context, Poll};
use std::time::{Duration, SystemTimeError};
use futures::{StreamExt, TryStreamExt};
use reqwest_retry::policies::ExponentialBackoff;
@@ -248,11 +249,16 @@ pub enum Error {
#[error("Failed to detect platform")]
Platform(#[from] uv_platform::Error),
#[error("Request failed after {retries} {subject}", subject = if *retries > 1 { "retries" } else { "retry" })]
#[error(
"Request failed after {retries} {subject} in {duration:.1}s",
subject = if *retries > 1 { "retries" } else { "retry" },
duration = duration.as_secs_f32()
)]
RetriedError {
#[source]
err: Box<Self>,
retries: u32,
duration: Duration,
},
#[error("Failed to fetch version manifest from: {url}")]
@@ -287,6 +293,9 @@ pub enum Error {
#[error("Unsupported archive format: {0}")]
UnsupportedArchiveFormat(String),
#[error(transparent)]
SystemTime(#[from] SystemTimeError),
}
impl Error {
@@ -352,6 +361,7 @@ pub async fn find_matching_version(
Err(Error::RetriedError {
err: Box::new(err),
retries: retry_state.total_retries(),
duration: retry_state.duration()?,
})
} else {
Err(err)
@@ -628,6 +638,7 @@ async fn download_and_unpack_with_retry(
Err(Error::RetriedError {
err: Box::new(err),
retries: retry_state.total_retries(),
duration: retry_state.duration()?,
})
} else {
Err(err)
@@ -678,6 +689,8 @@ async fn download_and_unpack(
return Err(Error::RetriedError {
err: Box::new(err),
retries,
// This value is overwritten in `download_and_unpack_with_retry`.
duration: Duration::default(),
});
}
return Err(err);
+6 -1
View File
@@ -4,7 +4,7 @@ use std::fmt::Write;
use std::num::ParseIntError;
use std::path::Path;
use std::sync::Arc;
use std::time::{Duration, SystemTime};
use std::time::{Duration, SystemTime, SystemTimeError};
use std::{env, io, iter};
use anyhow::anyhow;
@@ -1317,6 +1317,11 @@ impl RetryState {
self.total_retries
}
/// The total duration from the first request to the (failure) of the last request.
pub fn duration(&self) -> Result<Duration, SystemTimeError> {
self.start_time.elapsed()
}
/// Determines whether request should be retried.
///
/// Takes the number of retries from nested layers associated with the specific `err` type as
+44 -11
View File
@@ -1,3 +1,4 @@
use std::time::{Duration, Instant};
use std::{borrow::Cow, path::Path};
use futures::FutureExt;
@@ -105,7 +106,11 @@ pub enum CachedClientError<CallbackError: std::error::Error + 'static> {
Client(Error),
/// Track retries before a callback explicitly, as we can't attach them to the callback error
/// type.
Callback { retries: u32, err: CallbackError },
Callback {
retries: u32,
err: CallbackError,
duration: Duration,
},
}
impl<CallbackError: std::error::Error + 'static> CachedClientError<CallbackError> {
@@ -113,7 +118,15 @@ impl<CallbackError: std::error::Error + 'static> CachedClientError<CallbackError
fn with_retries(self, retries: u32) -> Self {
match self {
Self::Client(err) => Self::Client(err.with_retries(retries)),
Self::Callback { retries: _, err } => Self::Callback { retries, err },
Self::Callback {
retries: _,
err,
duration,
} => Self::Callback {
retries,
err,
duration,
},
}
}
@@ -151,9 +164,11 @@ impl<E: Into<Self> + std::error::Error + 'static> From<CachedClientError<E>> for
fn from(error: CachedClientError<E>) -> Self {
match error {
CachedClientError::Client(error) => error,
CachedClientError::Callback { retries, err } => {
Self::new(err.into().into_kind(), retries)
}
CachedClientError::Callback {
retries,
err,
duration,
} => Self::new(err.into().into_kind(), retries, duration),
}
}
}
@@ -261,6 +276,7 @@ impl CachedClient {
response_callback: Callback,
) -> Result<Payload::Target, CachedClientError<CallBackError>> {
let fresh_req = req.try_clone().expect("HTTP request must be cloneable");
let start = Instant::now();
let cached_response = if let Some(cached) = Self::read_cache(cache_entry).await {
self.send_cached(req, cache_control, cached)
.boxed_local()
@@ -339,6 +355,7 @@ impl CachedClient {
self.run_response_callback(
cache_entry,
cache_policy,
start,
response,
response_callback,
)
@@ -360,10 +377,11 @@ impl CachedClient {
cache_control: CacheControl<'_>,
response_callback: Callback,
) -> Result<Payload, CachedClientError<CallBackError>> {
let start = Instant::now();
let (response, cache_policy) = self.fresh_request(req, cache_control).await?;
let payload = self
.run_response_callback(cache_entry, cache_policy, response, async |resp| {
.run_response_callback(cache_entry, cache_policy, start, response, async |resp| {
let payload = response_callback(resp).await?;
Ok(SerdeCacheable { inner: payload })
})
@@ -384,9 +402,16 @@ impl CachedClient {
response_callback: Callback,
) -> Result<Payload::Target, CachedClientError<CallBackError>> {
let _ = fs_err::tokio::remove_file(&cache_entry.path()).await;
let start = Instant::now();
let (response, cache_policy) = self.fresh_request(req, cache_control).await?;
self.run_response_callback(cache_entry, cache_policy, response, response_callback)
.await
self.run_response_callback(
cache_entry,
cache_policy,
start,
response,
response_callback,
)
.await
}
async fn run_response_callback<
@@ -397,6 +422,7 @@ impl CachedClient {
&self,
cache_entry: &CacheEntry,
cache_policy: Option<Box<CachePolicy>>,
start: Instant,
response: Response,
response_callback: Callback,
) -> Result<Payload::Target, CachedClientError<CallBackError>> {
@@ -410,7 +436,11 @@ impl CachedClient {
let data = response_callback(response)
.boxed_local()
.await
.map_err(|err| CachedClientError::Callback { retries, err })?;
.map_err(|err| CachedClientError::Callback {
retries,
err,
duration: start.elapsed(),
})?;
let Some(cache_policy) = cache_policy else {
return Ok(data.into_target());
};
@@ -516,12 +546,13 @@ impl CachedClient {
) -> Result<CachedResponse, Error> {
let url = DisplaySafeUrl::from_url(req.url().clone());
debug!("Sending revalidation request for: {url}");
let start = Instant::now();
let mut response = self
.0
.execute(req)
.instrument(info_span!("revalidation_request", url = url.as_str()))
.await
.map_err(|err| Error::from_reqwest_middleware(url.clone(), err))?;
.map_err(|err| Error::from_reqwest_middleware(url.clone(), err, start))?;
trace!(
"Received response for revalidation request with status {} for: {}",
response.status(),
@@ -581,11 +612,12 @@ impl CachedClient {
let url = DisplaySafeUrl::from_url(req.url().clone());
debug!("Sending fresh {} request for: {}", req.method(), url);
let cache_policy_builder = CachePolicyBuilder::new(&req);
let start = Instant::now();
let mut response = self
.0
.execute(req)
.await
.map_err(|err| Error::from_reqwest_middleware(url.clone(), err))?;
.map_err(|err| Error::from_reqwest_middleware(url.clone(), err, start))?;
trace!(
"Received response for fresh request with status {} for: {}",
response.status(),
@@ -611,6 +643,7 @@ impl CachedClient {
return Err(Error::new(
ErrorKind::from_reqwest_with_problem_details(url, status_error, problem_details),
retry_count.unwrap_or_default(),
start.elapsed(),
));
}
+20 -6
View File
@@ -1,10 +1,12 @@
use std::fmt::{Display, Formatter};
use std::ops::Deref;
use std::path::PathBuf;
use std::time::{Duration, Instant};
use async_http_range_reader::AsyncHttpRangeReaderError;
use async_zip::error::ZipError;
use reqwest::Response;
use serde::Deserialize;
use std::fmt::{Display, Formatter};
use std::ops::Deref;
use std::path::PathBuf;
use tracing::warn;
use uv_cache::Error as CacheError;
@@ -114,6 +116,7 @@ impl ProblemDetails {
pub struct Error {
kind: Box<ErrorKind>,
retries: u32,
duration: Duration,
}
impl Display for Error {
@@ -121,9 +124,10 @@ impl Display for Error {
if self.retries > 0 {
write!(
f,
"Request failed after {retries} {subject}",
"Request failed after {retries} {subject} in {duration:.1}s",
retries = self.retries,
subject = if self.retries > 1 { "retries" } else { "retry" }
subject = if self.retries > 1 { "retries" } else { "retry" },
duration = self.duration.as_secs_f32(),
)
} else {
Display::fmt(&self.kind, f)
@@ -143,10 +147,11 @@ impl std::error::Error for Error {
impl Error {
/// Create a new [`Error`] with the given [`ErrorKind`] and number of retries.
pub fn new(kind: ErrorKind, retries: u32) -> Self {
pub fn new(kind: ErrorKind, retries: u32, duration: Duration) -> Self {
Self {
kind: Box::new(kind),
retries,
duration,
}
}
@@ -155,6 +160,12 @@ impl Error {
self.retries
}
/// Return the time taken for network requests, including retries, backoff and jitter,
/// before this error was returned.
pub fn duration(&self) -> Duration {
self.duration
}
/// Convert this error into an [`ErrorKind`].
pub fn into_kind(self) -> ErrorKind {
*self.kind
@@ -189,6 +200,7 @@ impl Error {
pub(crate) fn from_reqwest_middleware(
url: DisplaySafeUrl,
err: reqwest_middleware::Error,
start: Instant,
) -> Self {
if let reqwest_middleware::Error::Middleware(ref underlying) = err {
if let Some(offline_err) = underlying.downcast_ref::<OfflineError>() {
@@ -201,6 +213,7 @@ impl Error {
return Self::new(
ErrorKind::WrappedReqwestError(url, WrappedReqwestError::from(err)),
retries,
start.elapsed(),
);
}
}
@@ -316,6 +329,7 @@ impl From<ErrorKind> for Error {
Self {
kind: Box::new(kind),
retries: 0,
duration: Duration::default(),
}
}
}
+14 -2
View File
@@ -5,6 +5,7 @@ use std::path::{Path, PathBuf};
use std::pin::Pin;
use std::str::FromStr;
use std::task::{Context, Poll};
use std::time::{Duration, Instant, SystemTimeError};
use std::{env, io};
use futures::TryStreamExt;
@@ -54,11 +55,16 @@ pub enum Error {
TooManyParts(String),
#[error("Failed to download {0}")]
NetworkError(DisplaySafeUrl, #[source] WrappedReqwestError),
#[error("Request failed after {retries} {subject}", subject = if *retries > 1 { "retries" } else { "retry" })]
#[error(
"Request failed after {retries} {subject} in {duration:.1}s",
subject = if *retries > 1 { "retries" } else { "retry" },
duration = duration.as_secs_f32()
)]
NetworkErrorWithRetries {
#[source]
err: Box<Self>,
retries: u32,
duration: Duration,
},
#[error("Failed to download {0}")]
NetworkMiddlewareError(DisplaySafeUrl, #[source] anyhow::Error),
@@ -116,6 +122,8 @@ pub enum Error {
BuildVersion(#[from] BuildVersionError),
#[error("No download URL found for Python")]
NoPythonDownloadUrlFound,
#[error(transparent)]
SystemTime(#[from] SystemTimeError),
}
impl Error {
@@ -1183,6 +1191,7 @@ impl ManagedPythonDownload {
Err(Error::NetworkErrorWithRetries {
err: Box::new(err),
retries: retry_state.total_retries(),
duration: retry_state.duration()?,
})
} else {
Err(err)
@@ -1698,12 +1707,14 @@ impl Error {
url: DisplaySafeUrl,
err: reqwest::Error,
retries: Option<u32>,
start: Instant,
) -> Self {
let err = Self::NetworkError(url, WrappedReqwestError::from(err));
if let Some(retries) = retries {
Self::NetworkErrorWithRetries {
err: Box::new(err),
retries,
duration: start.elapsed(),
}
} else {
err
@@ -1815,6 +1826,7 @@ async fn read_url(
Ok((Either::Left(reader), Some(size)))
} else {
let start = Instant::now();
let response = client
.for_host(url)
.get(Url::from(url.clone()))
@@ -1830,7 +1842,7 @@ async fn read_url(
// Check the status code.
let response = response
.error_for_status()
.map_err(|err| Error::from_reqwest(url.clone(), err, retry_count))?;
.map_err(|err| Error::from_reqwest(url.clone(), err, retry_count, start))?;
let size = response.content_length();
let stream = response
+2 -1
View File
@@ -248,6 +248,7 @@ impl<Context: BuildContext> ResolverProvider for DefaultResolverProvider<'_, Con
Err(err) => match err {
uv_distribution::Error::Client(client) => {
let retries = client.retries();
let duration = client.duration();
match client.into_kind() {
uv_client::ErrorKind::Offline(_) => {
Ok(MetadataResponse::Unavailable(MetadataUnavailable::Offline))
@@ -262,7 +263,7 @@ impl<Context: BuildContext> ResolverProvider for DefaultResolverProvider<'_, Con
MetadataUnavailable::InvalidStructure(Arc::new(err)),
))
}
kind => Err(uv_client::Error::new(kind, retries).into()),
kind => Err(uv_client::Error::new(kind, retries, duration).into()),
}
}
uv_distribution::Error::WheelMetadataVersionMismatch { .. } => {
+3 -2
View File
@@ -13321,13 +13321,14 @@ async fn add_unexpected_error_code() -> Result<()> {
})?;
uv_snapshot!(context.filters(), context.add().arg("anyio").arg("--index").arg(server.uri())
.env(EnvVars::UV_TEST_NO_HTTP_RETRY_DELAY, "true"), @"
.env(EnvVars::UV_TEST_NO_HTTP_RETRY_DELAY, "true")
.env(EnvVars::UV_HTTP_RETRIES, "1"), @"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
error: Request failed after 3 retries
error: Request failed after 1 retry in [TIME]
Caused by: Failed to fetch: `http://[LOCALHOST]/anyio/`
Caused by: HTTP status server error (503 Service Unavailable) for url (http://[LOCALHOST]/anyio/)
"
+56 -67
View File
@@ -240,8 +240,7 @@ async fn simple_http_500() {
let (_server_drop_guard, mock_server_uri) = http_error_server().await;
let filters = vec![(mock_server_uri.as_str(), "[SERVER]")];
uv_snapshot!(filters, context
uv_snapshot!(context.filters(), context
.pip_install()
.arg("tqdm")
.arg("--index-url")
@@ -252,9 +251,9 @@ async fn simple_http_500() {
----- stdout -----
----- stderr -----
error: Request failed after 3 retries
Caused by: Failed to fetch: `[SERVER]/tqdm/`
Caused by: HTTP status server error (500 Internal Server Error) for url ([SERVER]/tqdm/)
error: Request failed after 3 retries in [TIME]
Caused by: Failed to fetch: `http://[LOCALHOST]/tqdm/`
Caused by: HTTP status server error (500 Internal Server Error) for url (http://[LOCALHOST]/tqdm/)
");
}
@@ -265,8 +264,7 @@ async fn simple_io_err() {
let (_server_drop_guard, mock_server_uri) = io_error_server().await;
let filters = vec![(mock_server_uri.as_str(), "[SERVER]")];
uv_snapshot!(filters, context
uv_snapshot!(context.filters(), context
.pip_install()
.arg("tqdm")
.arg("--index-url")
@@ -277,9 +275,9 @@ async fn simple_io_err() {
----- stdout -----
----- stderr -----
error: Request failed after 3 retries
Caused by: Failed to fetch: `[SERVER]/tqdm/`
Caused by: error sending request for url ([SERVER]/tqdm/)
error: Request failed after 3 retries in [TIME]
Caused by: Failed to fetch: `http://[LOCALHOST]/tqdm/`
Caused by: error sending request for url (http://[LOCALHOST]/tqdm/)
Caused by: client error (SendRequest)
Caused by: connection closed before message completed
");
@@ -292,8 +290,7 @@ async fn find_links_http_500() {
let (_server_drop_guard, mock_server_uri) = http_error_server().await;
let filters = vec![(mock_server_uri.as_str(), "[SERVER]")];
uv_snapshot!(filters, context
uv_snapshot!(context.filters(), context
.pip_install()
.arg("tqdm")
.arg("--no-index")
@@ -305,10 +302,10 @@ async fn find_links_http_500() {
----- stdout -----
----- stderr -----
error: Failed to read `--find-links` URL: [SERVER]/
Caused by: Request failed after 3 retries
Caused by: Failed to fetch: `[SERVER]/`
Caused by: HTTP status server error (500 Internal Server Error) for url ([SERVER]/)
error: Failed to read `--find-links` URL: http://[LOCALHOST]/
Caused by: Request failed after 3 retries in [TIME]
Caused by: Failed to fetch: `http://[LOCALHOST]/`
Caused by: HTTP status server error (500 Internal Server Error) for url (http://[LOCALHOST]/)
");
}
@@ -319,8 +316,7 @@ async fn find_links_io_error() {
let (_server_drop_guard, mock_server_uri) = io_error_server().await;
let filters = vec![(mock_server_uri.as_str(), "[SERVER]")];
uv_snapshot!(filters, context
uv_snapshot!(context.filters(), context
.pip_install()
.arg("tqdm")
.arg("--no-index")
@@ -332,10 +328,10 @@ async fn find_links_io_error() {
----- stdout -----
----- stderr -----
error: Failed to read `--find-links` URL: [SERVER]/
Caused by: Request failed after 3 retries
Caused by: Failed to fetch: `[SERVER]/`
Caused by: error sending request for url ([SERVER]/)
error: Failed to read `--find-links` URL: http://[LOCALHOST]/
Caused by: Request failed after 3 retries in [TIME]
Caused by: Failed to fetch: `http://[LOCALHOST]/`
Caused by: error sending request for url (http://[LOCALHOST]/)
Caused by: client error (SendRequest)
Caused by: connection closed before message completed
");
@@ -349,8 +345,7 @@ async fn find_links_mixed_error() {
let (_server_drop_guard, mock_server_uri) = mixed_error_server().await;
let filters = vec![(mock_server_uri.as_str(), "[SERVER]")];
uv_snapshot!(filters, context
uv_snapshot!(context.filters(), context
.pip_install()
.arg("tqdm")
.arg("--no-index")
@@ -362,10 +357,10 @@ async fn find_links_mixed_error() {
----- stdout -----
----- stderr -----
error: Failed to read `--find-links` URL: [SERVER]/
Caused by: Request failed after 3 retries
Caused by: Failed to fetch: `[SERVER]/`
Caused by: HTTP status server error (500 Internal Server Error) for url ([SERVER]/)
error: Failed to read `--find-links` URL: http://[LOCALHOST]/
Caused by: Request failed after 3 retries in [TIME]
Caused by: Failed to fetch: `http://[LOCALHOST]/`
Caused by: HTTP status server error (500 Internal Server Error) for url (http://[LOCALHOST]/)
");
}
@@ -380,8 +375,7 @@ async fn direct_url_http_500() {
let tqdm_url = format!(
"{mock_server_uri}/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl"
);
let filters = vec![(mock_server_uri.as_str(), "[SERVER]")];
uv_snapshot!(filters, context
uv_snapshot!(context.filters(), context
.pip_install()
.arg(format!("tqdm @ {tqdm_url}"))
.env(EnvVars::UV_TEST_NO_HTTP_RETRY_DELAY, "true"), @"
@@ -390,10 +384,10 @@ async fn direct_url_http_500() {
----- stdout -----
----- stderr -----
× Failed to download `tqdm @ [SERVER]/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl`
├─▶ Request failed after 3 retries
├─▶ Failed to fetch: `[SERVER]/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl`
╰─▶ HTTP status server error (500 Internal Server Error) for url ([SERVER]/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl)
× Failed to download `tqdm @ http://[LOCALHOST]/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl`
├─▶ Request failed after 3 retries in [TIME]
├─▶ Failed to fetch: `http://[LOCALHOST]/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl`
╰─▶ HTTP status server error (500 Internal Server Error) for url (http://[LOCALHOST]/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl)
");
}
@@ -407,8 +401,7 @@ async fn direct_url_io_error() {
let tqdm_url = format!(
"{mock_server_uri}/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl"
);
let filters = vec![(mock_server_uri.as_str(), "[SERVER]")];
uv_snapshot!(filters, context
uv_snapshot!(context.filters(), context
.pip_install()
.arg(format!("tqdm @ {tqdm_url}"))
.env(EnvVars::UV_TEST_NO_HTTP_RETRY_DELAY, "true"), @"
@@ -417,10 +410,10 @@ async fn direct_url_io_error() {
----- stdout -----
----- stderr -----
× Failed to download `tqdm @ [SERVER]/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl`
├─▶ Request failed after 3 retries
├─▶ Failed to fetch: `[SERVER]/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl`
├─▶ error sending request for url ([SERVER]/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl)
× Failed to download `tqdm @ http://[LOCALHOST]/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl`
├─▶ Request failed after 3 retries in [TIME]
├─▶ Failed to fetch: `http://[LOCALHOST]/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl`
├─▶ error sending request for url (http://[LOCALHOST]/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl)
├─▶ client error (SendRequest)
╰─▶ connection closed before message completed
");
@@ -437,8 +430,7 @@ async fn direct_url_mixed_error() {
let tqdm_url = format!(
"{mock_server_uri}/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl"
);
let filters = vec![(mock_server_uri.as_str(), "[SERVER]")];
uv_snapshot!(filters, context
uv_snapshot!(context.filters(), context
.pip_install()
.arg(format!("tqdm @ {tqdm_url}"))
.env(EnvVars::UV_TEST_NO_HTTP_RETRY_DELAY, "true"), @"
@@ -447,10 +439,10 @@ async fn direct_url_mixed_error() {
----- stdout -----
----- stderr -----
× Failed to download `tqdm @ [SERVER]/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl`
├─▶ Request failed after 3 retries
├─▶ Failed to fetch: `[SERVER]/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl`
╰─▶ HTTP status server error (500 Internal Server Error) for url ([SERVER]/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl)
× Failed to download `tqdm @ http://[LOCALHOST]/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl`
├─▶ Request failed after 3 retries in [TIME]
├─▶ Failed to fetch: `http://[LOCALHOST]/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl`
╰─▶ HTTP status server error (500 Internal Server Error) for url (http://[LOCALHOST]/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl)
");
}
@@ -493,8 +485,7 @@ async fn python_install_http_500() {
let python_downloads_json = write_python_downloads_json(&context, &mock_server_uri);
let filters = vec![(mock_server_uri.as_str(), "[SERVER]")];
uv_snapshot!(filters, context
uv_snapshot!(context.filters(), context
.python_install()
.arg("cpython-3.10.0-darwin-aarch64-none")
.arg("--python-downloads-json-url")
@@ -505,10 +496,10 @@ async fn python_install_http_500() {
----- stdout -----
----- stderr -----
error: Failed to install cpython-3.10.0-macos-aarch64-none
Caused by: Request failed after 3 retries
Caused by: Failed to download [SERVER]/astral-sh/python-build-standalone/releases/download/20211017/cpython-3.10.0-aarch64-apple-darwin-pgo%2Blto-20211017T1616.tar.zst
Caused by: HTTP status server error (500 Internal Server Error) for url ([SERVER]/astral-sh/python-build-standalone/releases/download/20211017/cpython-3.10.0-aarch64-apple-darwin-pgo%2Blto-20211017T1616.tar.zst)
error: Failed to install cpython-3.10.0-[PLATFORM]
Caused by: Request failed after 3 retries in [TIME]
Caused by: Failed to download http://[LOCALHOST]/astral-sh/python-build-standalone/releases/download/20211017/cpython-3.10.0-[PLATFORM]-pgo%2Blto-20211017T1616.tar.zst
Caused by: HTTP status server error (500 Internal Server Error) for url (http://[LOCALHOST]/astral-sh/python-build-standalone/releases/download/20211017/cpython-3.10.0-[PLATFORM]-pgo%2Blto-20211017T1616.tar.zst)
");
}
@@ -524,8 +515,7 @@ async fn python_install_io_error() {
let python_downloads_json = write_python_downloads_json(&context, &mock_server_uri);
let filters = vec![(mock_server_uri.as_str(), "[SERVER]")];
uv_snapshot!(filters, context
uv_snapshot!(context.filters(), context
.python_install()
.arg("cpython-3.10.0-darwin-aarch64-none")
.arg("--python-downloads-json-url")
@@ -536,10 +526,10 @@ async fn python_install_io_error() {
----- stdout -----
----- stderr -----
error: Failed to install cpython-3.10.0-macos-aarch64-none
Caused by: Request failed after 3 retries
Caused by: Failed to download [SERVER]/astral-sh/python-build-standalone/releases/download/20211017/cpython-3.10.0-aarch64-apple-darwin-pgo%2Blto-20211017T1616.tar.zst
Caused by: error sending request for url ([SERVER]/astral-sh/python-build-standalone/releases/download/20211017/cpython-3.10.0-aarch64-apple-darwin-pgo%2Blto-20211017T1616.tar.zst)
error: Failed to install cpython-3.10.0-[PLATFORM]
Caused by: Request failed after 3 retries in [TIME]
Caused by: Failed to download http://[LOCALHOST]/astral-sh/python-build-standalone/releases/download/20211017/cpython-3.10.0-[PLATFORM]-pgo%2Blto-20211017T1616.tar.zst
Caused by: error sending request for url (http://[LOCALHOST]/astral-sh/python-build-standalone/releases/download/20211017/cpython-3.10.0-[PLATFORM]-pgo%2Blto-20211017T1616.tar.zst)
Caused by: client error (SendRequest)
Caused by: connection closed before message completed
");
@@ -611,7 +601,7 @@ async fn install_http_retries() {
----- stdout -----
----- stderr -----
error: Request failed after 5 retries
error: Request failed after 5 retries in [TIME]
Caused by: Failed to fetch: `http://[LOCALHOST]/anyio/`
Caused by: HTTP status server error (503 Service Unavailable) for url (http://[LOCALHOST]/anyio/)
"
@@ -643,7 +633,7 @@ async fn install_http_retry_low_level() {
----- stdout -----
----- stderr -----
error: Request failed after 1 retry
error: Request failed after 1 retry in [TIME]
Caused by: Failed to fetch: `http://[LOCALHOST]/anyio/`
Caused by: error sending request for url (http://[LOCALHOST]/anyio/)
Caused by: client error (SendRequest)
@@ -684,8 +674,7 @@ async fn rfc9457_problem_details_license_violation() {
let mock_server_uri = server.uri();
let tqdm_url = format!("{mock_server_uri}/packages/tqdm-4.67.1-py3-none-any.whl");
let filters = vec![(mock_server_uri.as_str(), "[SERVER]")];
uv_snapshot!(filters, context
uv_snapshot!(context.filters(), context
.pip_install()
.arg(format!("tqdm @ {tqdm_url}")), @"
success: false
@@ -693,10 +682,10 @@ async fn rfc9457_problem_details_license_violation() {
----- stdout -----
----- stderr -----
× Failed to download `tqdm @ [SERVER]/packages/tqdm-4.67.1-py3-none-any.whl`
├─▶ Failed to fetch: `[SERVER]/packages/tqdm-4.67.1-py3-none-any.whl`
× Failed to download `tqdm @ http://[LOCALHOST]/packages/tqdm-4.67.1-py3-none-any.whl`
├─▶ Failed to fetch: `http://[LOCALHOST]/packages/tqdm-4.67.1-py3-none-any.whl`
├─▶ Server message: License Compliance Issue, This package version has a license that violates organizational policy.
╰─▶ HTTP status client error (403 Forbidden) for url ([SERVER]/packages/tqdm-4.67.1-py3-none-any.whl)
╰─▶ HTTP status client error (403 Forbidden) for url (http://[LOCALHOST]/packages/tqdm-4.67.1-py3-none-any.whl)
");
}
@@ -1079,7 +1068,7 @@ async fn retry_read_timeout_index() {
----- stdout -----
----- stderr -----
error: Request failed after 1 retry
error: Request failed after 1 retry in [TIME]
Caused by: Failed to fetch: `http://[LOCALHOST]/tqdm/`
Caused by: error decoding response body
Caused by: request or response body error
@@ -1105,7 +1094,7 @@ async fn retry_read_timeout_stream() {
----- stderr -----
× Failed to download `tqdm @ http://[LOCALHOST]/tqdm-0.1-py3-none-any.whl`
├─▶ Request failed after 1 retry
├─▶ Request failed after 1 retry in [TIME]
├─▶ Failed to read metadata: `http://[LOCALHOST]/tqdm-0.1-py3-none-any.whl`
├─▶ Failed to read from zip file
├─▶ an upstream reader returned an error: Failed to download distribution due to network timeout. Try increasing UV_HTTP_TIMEOUT (current value: [TIME]).
+1 -1
View File
@@ -14326,7 +14326,7 @@ fn warn_on_lzma_wheel() {
----- stderr -----
× Failed to download `futzed-lzma @ https://github.com/astral-sh/futzed-wheels/releases/download/v2026.02.09.2/futzed_lzma-0.1.0-py3-none-any.whl`
Request failed after 3 retries
Request failed after 3 retries in [TIME]
Failed to read metadata: `https://github.com/astral-sh/futzed-wheels/releases/download/v2026.02.09.2/futzed_lzma-0.1.0-py3-none-any.whl`
Failed to read from zip file
an upstream reader returned an error: stream/file format not recognized