From 3168107df942dc71605e4fa25069569a43d467e9 Mon Sep 17 00:00:00 2001 From: Fintan Halpenny Date: Fri, 19 Dec 2025 10:16:55 +0000 Subject: [PATCH] fetch: surface underlying I/O error The message that is returned by `gix-transport` for I/O errors can be unhelpful, since it does not provide the reason for what happened. Instead, surface the error so that it provides more detail for logging. --- .../examples/rad-init-private-clone.md | 2 +- crates/radicle-fetch/src/lib.rs | 46 +++++++++++++++---- crates/radicle-fetch/src/transport.rs | 4 +- 3 files changed, 40 insertions(+), 12 deletions(-) diff --git a/crates/radicle-cli/examples/rad-init-private-clone.md b/crates/radicle-cli/examples/rad-init-private-clone.md index a5249881..852538d9 100644 --- a/crates/radicle-cli/examples/rad-init-private-clone.md +++ b/crates/radicle-cli/examples/rad-init-private-clone.md @@ -10,7 +10,7 @@ $ rad clone rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu --seed z6MknSLrJoTcukLrE435hVNQT4J Fetching rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu from the network, found 1 potential seed(s). ✗ Target not met: could not fetch from [z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi], and required 1 more seed(s) ! Warning: Failed to fetch from 1 seed(s). -! Warning: z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi: failed to perform fetch handshake: [..] +! Warning: z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi: an I/O error occurred during the fetch handshake (connection reset) ✗ Error: no seeds found for rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu ``` diff --git a/crates/radicle-fetch/src/lib.rs b/crates/radicle-fetch/src/lib.rs index cd1c2007..a53a0bfe 100644 --- a/crates/radicle-fetch/src/lib.rs +++ b/crates/radicle-fetch/src/lib.rs @@ -9,6 +9,7 @@ mod refs; mod stage; mod state; +use std::io; use std::time::Instant; use gix_protocol::handshake; @@ -28,8 +29,8 @@ use thiserror::Error; #[derive(Debug, Error)] pub enum Error { - #[error("failed to perform fetch handshake: {0}")] - Handshake(#[from] Box), + #[error(transparent)] + Handshake(Box), #[error("failed to load `rad/id`")] Identity { #[source] @@ -43,6 +44,20 @@ pub enum Error { ReplicateSelf, } +impl From for Error { + fn from(err: HandshakeError) -> Self { + Self::Handshake(Box::new(err)) + } +} + +#[derive(Debug, Error)] +pub enum HandshakeError { + #[error("failed to perform fetch handshake: {0}")] + Gix(handshake::Error), + #[error("an I/O error occurred during the fetch handshake ({0})")] + Io(io::Error), +} + /// Pull changes from the `remote`. /// /// It is expected that the local peer has a copy of the repository @@ -127,11 +142,24 @@ fn perform_handshake(handle: &mut Handle) -> Result HandshakeError { + let err = match err { + handshake::Error::Transport(error) => match error { + gix_transport::client::Error::Io(error) => HandshakeError::Io(error), + err => HandshakeError::Gix(handshake::Error::Transport(err)), + }, + err => { + log::warn!(target: "fetch", "Failed to perform handshake: {err}"); + HandshakeError::Gix(err) + } + }; + log::warn!(target: "fetch", "{err}"); + err } diff --git a/crates/radicle-fetch/src/transport.rs b/crates/radicle-fetch/src/transport.rs index d46c7ec5..f3a46b6b 100644 --- a/crates/radicle-fetch/src/transport.rs +++ b/crates/radicle-fetch/src/transport.rs @@ -89,7 +89,8 @@ where } /// Perform the handshake with the server side. - pub(crate) fn handshake(&mut self) -> Result> { + #[allow(clippy::result_large_err)] + pub(crate) fn handshake(&mut self) -> Result { log::trace!(target: "fetch", "Performing handshake for {}", self.repo); let (read, write) = self.stream.open(); gix_protocol::fetch::handshake( @@ -98,7 +99,6 @@ where vec![], &mut progress::Discard, ) - .map_err(Box::new) } /// Perform ls-refs with the server side.