From c08fd438ecce6c75834bf884ae49cd3574f7e62a Mon Sep 17 00:00:00 2001 From: Kyle Kelley Date: Wed, 11 Mar 2026 01:12:18 -0700 Subject: [PATCH] languages: Validate pylsp binary before returning from check_if_user_installed (#51034) Run `pylsp --version` via `delegate.try_exec()` in both branches of `PyLspAdapter::check_if_user_installed` before returning the binary. If execution fails (broken shebang, missing interpreter, etc.), log a warning and return None so the system falls through gracefully instead of surfacing an error dialog. This matches the existing validation pattern used by TyLspAdapter and RuffLspAdapter in their `fetch_server_binary` implementations. No idea if this closes an issue but it sure was annoying on a system where I deleted the pylsp environment I had. It surprised me too since I had pylsp disabled in settings. Release Notes: - Fixed detection of when `pylsp` is not installed properly on a user's system so that it doesn't get launched as an LSP when it doesn't exist. --- crates/languages/src/python.rs | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/crates/languages/src/python.rs b/crates/languages/src/python.rs index 95bfc798414f5d3629e1ea46f54d14a7ed58a8d4..078db5ba027c4d089b7c2f62cbd7e8468e526171 100644 --- a/crates/languages/src/python.rs +++ b/crates/languages/src/python.rs @@ -1846,6 +1846,17 @@ impl LspInstaller for PyLspAdapter { ) -> Option { if let Some(pylsp_bin) = delegate.which(Self::SERVER_NAME.as_ref()).await { let env = delegate.shell_env().await; + delegate + .try_exec(LanguageServerBinary { + path: pylsp_bin.clone(), + arguments: vec!["--version".into()], + env: Some(env.clone()), + }) + .await + .inspect_err(|err| { + log::warn!("failed to validate user-installed pylsp at {pylsp_bin:?}: {err:#}") + }) + .ok()?; Some(LanguageServerBinary { path: pylsp_bin, env: Some(env), @@ -1854,7 +1865,21 @@ impl LspInstaller for PyLspAdapter { } else { let toolchain = toolchain?; let pylsp_path = Path::new(toolchain.path.as_ref()).parent()?.join("pylsp"); - pylsp_path.exists().then(|| LanguageServerBinary { + if !pylsp_path.exists() { + return None; + } + delegate + .try_exec(LanguageServerBinary { + path: toolchain.path.to_string().into(), + arguments: vec![pylsp_path.clone().into(), "--version".into()], + env: None, + }) + .await + .inspect_err(|err| { + log::warn!("failed to validate toolchain pylsp at {pylsp_path:?}: {err:#}") + }) + .ok()?; + Some(LanguageServerBinary { path: toolchain.path.to_string().into(), arguments: vec![pylsp_path.into()], env: None,