From 8d555271ac473d42d668affcb306357a61377da1 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Tue, 20 Jan 2026 12:12:16 +0100 Subject: [PATCH] remote: Fix connecting to remote with running server failing (#47203) Follow up to https://github.com/zed-industries/zed/pull/47200, fixing an edge case where if the heartbeat failure disconnects us we get stuck in an unconnectable state Release Notes: - N/A *or* Added/Fixed/Improved ... --- crates/remote/src/remote_client.rs | 12 +++-- crates/remote_server/src/unix.rs | 86 ++++++++++++++++++------------ 2 files changed, 58 insertions(+), 40 deletions(-) diff --git a/crates/remote/src/remote_client.rs b/crates/remote/src/remote_client.rs index fa5b41b38fed610e6741b089615da0a54c8a6c87..4f22ef6bd015483621d6ccc12b4849677f7fa6fd 100644 --- a/crates/remote/src/remote_client.rs +++ b/crates/remote/src/remote_client.rs @@ -442,18 +442,20 @@ impl RemoteClient { return Err(error); } Err(_) => { - let mut error = - "remote client did not become ready within the timeout".to_owned(); + let mut error = String::new(); if let Some(status) = io_task.now_or_never() { + error.push_str("Client exited with "); match status { Ok(exit_code) => { - error.push_str(&format!(", exit_code={exit_code:?}")) + error.push_str(&format!(" exit_code {exit_code:?}")) } - Err(e) => error.push_str(&format!(", error={e:?}")), + Err(e) => error.push_str(&format!(" error {e:?}")), } + } else { + error.push_str("client did not become ready within the timeout"); } let error = anyhow::anyhow!("{error}"); - log::error!("failed to establish connection: {}", error); + log::error!("failed to establish connection: {error}"); return Err(error); } } diff --git a/crates/remote_server/src/unix.rs b/crates/remote_server/src/unix.rs index 56a6aa45cf5ef5ff6d38388066839036d7c39311..90bd00d1e3b02c5e14d6dc5345644e021df19624 100644 --- a/crates/remote_server/src/unix.rs +++ b/crates/remote_server/src/unix.rs @@ -578,34 +578,34 @@ impl ServerPaths { #[derive(Debug, Error)] pub enum ExecuteProxyError { - #[error("Failed to init server paths")] + #[error("Failed to init server paths: {0:#}")] ServerPath(#[from] ServerPathError), #[error(transparent)] ServerNotRunning(#[from] ProxyLaunchError), - #[error("Failed to check PidFile '{path}'")] + #[error("Failed to check PidFile '{path}': {source:#}")] CheckPidFile { #[source] source: CheckPidError, path: PathBuf, }, - #[error("Failed to kill existing server with pid '{pid}'")] + #[error("Failed to kill existing server with pid '{pid}': {source:#}")] KillRunningServer { #[source] source: std::io::Error, pid: u32, }, - #[error("failed to spawn server")] + #[error("failed to spawn server: {0:#}")] SpawnServer(#[source] SpawnServerError), - #[error("stdin_task failed")] + #[error("stdin_task failed: {0:#}")] StdinTask(#[source] anyhow::Error), - #[error("stdout_task failed")] + #[error("stdout_task failed: {0:#}")] StdoutTask(#[source] anyhow::Error), - #[error("stderr_task failed")] + #[error("stderr_task failed: {0:#}")] StderrTask(#[source] anyhow::Error), } @@ -657,50 +657,66 @@ pub(crate) fn execute_proxy( Some(server_pid) => Ok(server_pid), } } else { - match server_pid { - Some(pid) => { - log::info!( - "proxy found server already running with PID {}. Killing process and cleaning up files...", - pid - ); - kill_running_server(pid, &server_paths).await?; - Ok(pid) - } - None => { - spawn_server(&server_paths) - .await - .map_err(ExecuteProxyError::SpawnServer)?; - std::fs::read_to_string(&server_paths.pid_file) - .and_then(|contents| { - contents.parse::().map_err(|_| { - std::io::Error::new( - std::io::ErrorKind::InvalidData, - "Invalid PID file contents", - ) - }) - }) - .map_err(SpawnServerError::ProcessStatus) - .map_err(ExecuteProxyError::SpawnServer) - } + if let Some(pid) = server_pid { + log::info!( + "proxy found server already running with PID {}. Killing process and cleaning up files...", + pid + ); + kill_running_server(pid, &server_paths).await?; } + spawn_server(&server_paths) + .await + .map_err(ExecuteProxyError::SpawnServer)?; + std::fs::read_to_string(&server_paths.pid_file) + .and_then(|contents| { + contents.parse::().map_err(|_| { + std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Invalid PID file contents", + ) + }) + }) + .map_err(SpawnServerError::ProcessStatus) + .map_err(ExecuteProxyError::SpawnServer) } })?; let stdin_task = smol::spawn(async move { let stdin = smol::Unblock::new(std::io::stdin()); - let stream = smol::net::unix::UnixStream::connect(&server_paths.stdin_socket).await?; + let stream = smol::net::unix::UnixStream::connect(&server_paths.stdin_socket) + .await + .with_context(|| { + format!( + "Failed to connect to stdin socket {}", + server_paths.stdin_socket.display() + ) + })?; handle_io(stdin, stream, "stdin").await }); let stdout_task: smol::Task> = smol::spawn(async move { let stdout = smol::Unblock::new(std::io::stdout()); - let stream = smol::net::unix::UnixStream::connect(&server_paths.stdout_socket).await?; + let stream = smol::net::unix::UnixStream::connect(&server_paths.stdout_socket) + .await + .with_context(|| { + format!( + "Failed to connect to stdout socket {}", + server_paths.stdout_socket.display() + ) + })?; handle_io(stream, stdout, "stdout").await }); let stderr_task: smol::Task> = smol::spawn(async move { let mut stderr = smol::Unblock::new(std::io::stderr()); - let mut stream = smol::net::unix::UnixStream::connect(&server_paths.stderr_socket).await?; + let mut stream = smol::net::unix::UnixStream::connect(&server_paths.stderr_socket) + .await + .with_context(|| { + format!( + "Failed to connect to stderr socket {}", + server_paths.stderr_socket.display() + ) + })?; let mut stderr_buffer = vec![0; 2048]; loop { match stream