diff --git a/src/api/errors/mod.rs b/src/api/errors/mod.rs index 230a920ff7..1b7be0adae 100644 --- a/src/api/errors/mod.rs +++ b/src/api/errors/mod.rs @@ -14,17 +14,12 @@ pub(super) struct ProjectRenamedError(pub(super) String); pub(super) type ApiResult = Result; #[derive(Debug, thiserror::Error)] -#[error("request failed with retryable status code {}", .body.status)] -pub(super) struct RetryError { - body: ApiResponse, -} - -impl RetryError { - pub fn new(body: ApiResponse) -> Self { - Self { body } - } - - pub fn into_body(self) -> ApiResponse { - self.body - } +pub(super) enum RetryError { + #[error("request failed with retryable status code {}", body.status)] + Status { body: ApiResponse }, + #[error("request failed with retryable error: {source}")] + ApiError { + #[from] + source: ApiError, + }, } diff --git a/src/api/mod.rs b/src/api/mod.rs index 7dce776cae..09165f28e3 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -15,6 +15,7 @@ mod serialization; use std::borrow::Cow; use std::cell::RefCell; use std::collections::HashMap; +use std::error::Error as _; #[cfg(any(target_os = "macos", not(feature = "managed")))] use std::fs::File; use std::io::{self, Read as _, Write}; @@ -1313,11 +1314,27 @@ impl ApiRequest { debug!("retry number {retry_number}, max retries: {max_retries}"); *retry_number += 1; - let mut rv = self.send_into(&mut out)?; + let mut rv = match self.send_into(&mut out) { + Ok(rv) => rv, + Err(err) => { + let is_retryable_dns = err + .source() + .and_then(|s| s.downcast_ref::()) + .is_some_and(|e| e.is_couldnt_resolve_host()); + + // Wrap DNS errors in a RetryError so they get retried + if is_retryable_dns { + anyhow::bail!(RetryError::from(err)); + } + + anyhow::bail!(err); + } + }; + rv.body = Some(out); if RETRY_STATUS_CODES.contains(&rv.status) { - anyhow::bail!(RetryError::new(rv)); + anyhow::bail!(RetryError::Status { body: rv }); } Ok(rv) @@ -1336,7 +1353,8 @@ impl ApiRequest { }) .call() .or_else(|err| match err.downcast::() { - Ok(err) => Ok(err.into_body()), + Ok(RetryError::Status { body }) => Ok(body), + Ok(RetryError::ApiError { source }) => Err(source), Err(err) => Err(ApiError::with_source(ApiErrorKind::RequestFailed, err)), }) }