From 439216c12c3eb144846b7b0173f821c2b61e93ca Mon Sep 17 00:00:00 2001 From: link2xt Date: Wed, 13 May 2026 17:11:42 +0200 Subject: [PATCH] feat: log all connection attempt errors instead of the first one We log all connection attempts errors as they fail already, but once all attempts are exhausted, we only log the first error without specifying which address failed. The first error is frequently the least interesting "Network is unreachable (os error 101)" that happens when trying to connect to IPv6 address from a network that does not support IPv6. To make reading the logs easier, log all errors together with the addresses again once all connection attempts are exhausted. Then it will be visible that IPv6 failed with "Network is unreachable (os error 101)" and IPv4 failed with "Connection timeout: deadline has elapsed" or similar error. Before the change error looked like this: IMAP failed to connect to example.org:143:starttls: Connection failure: Network is unreachable (os error 101). With the change the error looks like this: IMAP failed to connect to example.org:143:starttls: All connection attempts failed: Connection to [***::1]:143 failed: Network is unreachable (os error 101); Connection to [***::2]:143 failed: Network is unreachable (os error 101); Connection to x.x.x.1:143 timed out: deadline has elapsed; Connection to x.x.x.2:143 timed out: deadline has elapsed; Connection to x.x.x.3:143 timed out: deadline has elapsed. --- src/net.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/net.rs b/src/net.rs index c46f26017..b96c5dac0 100644 --- a/src/net.rs +++ b/src/net.rs @@ -109,8 +109,8 @@ pub(crate) async fn connect_tcp_inner( ) -> Result>>> { let tcp_stream = timeout(TIMEOUT, TcpStream::connect(addr)) .await - .context("Connection timeout")? - .context("Connection failure")?; + .with_context(|| format!("Connection to {addr} timed out"))? + .with_context(|| format!("Connection to {addr} failed"))?; // Disable Nagle's algorithm. tcp_stream.set_nodelay(true)?; @@ -180,7 +180,7 @@ where delay_set.spawn(tokio::time::sleep(delay)); } - let mut first_error = None; + let mut all_errors = Vec::new(); let res = loop { if let Some(fut) = futures.next() { @@ -200,7 +200,7 @@ where } Ok(Err(err)) => { // Some connection attempt failed. - first_error.get_or_insert(err); + all_errors.push(err); } Err(err) => { break Err(err); @@ -211,9 +211,11 @@ where // Out of connection attempts. // // Break out of the loop and return error. - break Err( - first_error.unwrap_or_else(|| format_err!("No connection attempts were made")) - ); + break if all_errors.is_empty() { + Err(format_err!("No connection attempts were made")) + } else { + Err(format_err!("All connection attempts failed: {}", all_errors.into_iter().map(|err| format!("{err:#}")).collect::>().join("; "))) + }; } } },